roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 
6078                 for(var i = 0; i < len; i++){
6079                     var args = Array.prototype.slice.call(arguments, 0);
6080                     args.push(ls.options);
6081                     var l = ls[i];
6082                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6083                         this.firing = false;
6084                         return false;
6085                     }
6086                 }
6087                 this.firing = false;
6088             }
6089             return true;
6090         }
6091     };
6092 })();/*
6093  * RooJS Library 
6094  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6095  *
6096  * Licence LGPL 
6097  *
6098  */
6099  
6100 /**
6101  * @class Roo.Document
6102  * @extends Roo.util.Observable
6103  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6104  * 
6105  * @param {Object} config the methods and properties of the 'base' class for the application.
6106  * 
6107  *  Generic Page handler - implement this to start your app..
6108  * 
6109  * eg.
6110  *  MyProject = new Roo.Document({
6111         events : {
6112             'load' : true // your events..
6113         },
6114         listeners : {
6115             'ready' : function() {
6116                 // fired on Roo.onReady()
6117             }
6118         }
6119  * 
6120  */
6121 Roo.Document = function(cfg) {
6122      
6123     this.addEvents({ 
6124         'ready' : true
6125     });
6126     Roo.util.Observable.call(this,cfg);
6127     
6128     var _this = this;
6129     
6130     Roo.onReady(function() {
6131         _this.fireEvent('ready');
6132     },null,false);
6133     
6134     
6135 }
6136
6137 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6138  * Based on:
6139  * Ext JS Library 1.1.1
6140  * Copyright(c) 2006-2007, Ext JS, LLC.
6141  *
6142  * Originally Released Under LGPL - original licence link has changed is not relivant.
6143  *
6144  * Fork - LGPL
6145  * <script type="text/javascript">
6146  */
6147
6148 /**
6149  * @class Roo.EventManager
6150  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6151  * several useful events directly.
6152  * See {@link Roo.EventObject} for more details on normalized event objects.
6153  * @singleton
6154  */
6155 Roo.EventManager = function(){
6156     var docReadyEvent, docReadyProcId, docReadyState = false;
6157     var resizeEvent, resizeTask, textEvent, textSize;
6158     var E = Roo.lib.Event;
6159     var D = Roo.lib.Dom;
6160
6161     
6162     
6163
6164     var fireDocReady = function(){
6165         if(!docReadyState){
6166             docReadyState = true;
6167             Roo.isReady = true;
6168             if(docReadyProcId){
6169                 clearInterval(docReadyProcId);
6170             }
6171             if(Roo.isGecko || Roo.isOpera) {
6172                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6173             }
6174             if(Roo.isIE){
6175                 var defer = document.getElementById("ie-deferred-loader");
6176                 if(defer){
6177                     defer.onreadystatechange = null;
6178                     defer.parentNode.removeChild(defer);
6179                 }
6180             }
6181             if(docReadyEvent){
6182                 docReadyEvent.fire();
6183                 docReadyEvent.clearListeners();
6184             }
6185         }
6186     };
6187     
6188     var initDocReady = function(){
6189         docReadyEvent = new Roo.util.Event();
6190         if(Roo.isGecko || Roo.isOpera) {
6191             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6192         }else if(Roo.isIE){
6193             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6194             var defer = document.getElementById("ie-deferred-loader");
6195             defer.onreadystatechange = function(){
6196                 if(this.readyState == "complete"){
6197                     fireDocReady();
6198                 }
6199             };
6200         }else if(Roo.isSafari){ 
6201             docReadyProcId = setInterval(function(){
6202                 var rs = document.readyState;
6203                 if(rs == "complete") {
6204                     fireDocReady();     
6205                  }
6206             }, 10);
6207         }
6208         // no matter what, make sure it fires on load
6209         E.on(window, "load", fireDocReady);
6210     };
6211
6212     var createBuffered = function(h, o){
6213         var task = new Roo.util.DelayedTask(h);
6214         return function(e){
6215             // create new event object impl so new events don't wipe out properties
6216             e = new Roo.EventObjectImpl(e);
6217             task.delay(o.buffer, h, null, [e]);
6218         };
6219     };
6220
6221     var createSingle = function(h, el, ename, fn){
6222         return function(e){
6223             Roo.EventManager.removeListener(el, ename, fn);
6224             h(e);
6225         };
6226     };
6227
6228     var createDelayed = function(h, o){
6229         return function(e){
6230             // create new event object impl so new events don't wipe out properties
6231             e = new Roo.EventObjectImpl(e);
6232             setTimeout(function(){
6233                 h(e);
6234             }, o.delay || 10);
6235         };
6236     };
6237     var transitionEndVal = false;
6238     
6239     var transitionEnd = function()
6240     {
6241         if (transitionEndVal) {
6242             return transitionEndVal;
6243         }
6244         var el = document.createElement('div');
6245
6246         var transEndEventNames = {
6247             WebkitTransition : 'webkitTransitionEnd',
6248             MozTransition    : 'transitionend',
6249             OTransition      : 'oTransitionEnd otransitionend',
6250             transition       : 'transitionend'
6251         };
6252     
6253         for (var name in transEndEventNames) {
6254             if (el.style[name] !== undefined) {
6255                 transitionEndVal = transEndEventNames[name];
6256                 return  transitionEndVal ;
6257             }
6258         }
6259     }
6260     
6261
6262     var listen = function(element, ename, opt, fn, scope){
6263         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6264         fn = fn || o.fn; scope = scope || o.scope;
6265         var el = Roo.getDom(element);
6266         
6267         
6268         if(!el){
6269             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6270         }
6271         
6272         if (ename == 'transitionend') {
6273             ename = transitionEnd();
6274         }
6275         var h = function(e){
6276             e = Roo.EventObject.setEvent(e);
6277             var t;
6278             if(o.delegate){
6279                 t = e.getTarget(o.delegate, el);
6280                 if(!t){
6281                     return;
6282                 }
6283             }else{
6284                 t = e.target;
6285             }
6286             if(o.stopEvent === true){
6287                 e.stopEvent();
6288             }
6289             if(o.preventDefault === true){
6290                e.preventDefault();
6291             }
6292             if(o.stopPropagation === true){
6293                 e.stopPropagation();
6294             }
6295
6296             if(o.normalized === false){
6297                 e = e.browserEvent;
6298             }
6299
6300             fn.call(scope || el, e, t, o);
6301         };
6302         if(o.delay){
6303             h = createDelayed(h, o);
6304         }
6305         if(o.single){
6306             h = createSingle(h, el, ename, fn);
6307         }
6308         if(o.buffer){
6309             h = createBuffered(h, o);
6310         }
6311         
6312         fn._handlers = fn._handlers || [];
6313         
6314         
6315         fn._handlers.push([Roo.id(el), ename, h]);
6316         
6317         
6318          
6319         E.on(el, ename, h);
6320         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6321             el.addEventListener("DOMMouseScroll", h, false);
6322             E.on(window, 'unload', function(){
6323                 el.removeEventListener("DOMMouseScroll", h, false);
6324             });
6325         }
6326         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6327             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6328         }
6329         return h;
6330     };
6331
6332     var stopListening = function(el, ename, fn){
6333         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6334         if(hds){
6335             for(var i = 0, len = hds.length; i < len; i++){
6336                 var h = hds[i];
6337                 if(h[0] == id && h[1] == ename){
6338                     hd = h[2];
6339                     hds.splice(i, 1);
6340                     break;
6341                 }
6342             }
6343         }
6344         E.un(el, ename, hd);
6345         el = Roo.getDom(el);
6346         if(ename == "mousewheel" && el.addEventListener){
6347             el.removeEventListener("DOMMouseScroll", hd, false);
6348         }
6349         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6350             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6351         }
6352     };
6353
6354     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6355     
6356     var pub = {
6357         
6358         
6359         /** 
6360          * Fix for doc tools
6361          * @scope Roo.EventManager
6362          */
6363         
6364         
6365         /** 
6366          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6367          * object with a Roo.EventObject
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    An object that becomes the scope of the handler
6370          * @param {boolean}  override If true, the obj passed in becomes
6371          *                             the execution scope of the listener
6372          * @return {Function} The wrapped function
6373          * @deprecated
6374          */
6375         wrap : function(fn, scope, override){
6376             return function(e){
6377                 Roo.EventObject.setEvent(e);
6378                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6379             };
6380         },
6381         
6382         /**
6383      * Appends an event handler to an element (shorthand for addListener)
6384      * @param {String/HTMLElement}   element        The html element or id to assign the
6385      * @param {String}   eventName The type of event to listen for
6386      * @param {Function} handler The method the event invokes
6387      * @param {Object}   scope (optional) The scope in which to execute the handler
6388      * function. The handler function's "this" context.
6389      * @param {Object}   options (optional) An object containing handler configuration
6390      * properties. This may contain any of the following properties:<ul>
6391      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6392      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6393      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6394      * <li>preventDefault {Boolean} True to prevent the default action</li>
6395      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6396      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6397      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6398      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6399      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6400      * by the specified number of milliseconds. If the event fires again within that time, the original
6401      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6402      * </ul><br>
6403      * <p>
6404      * <b>Combining Options</b><br>
6405      * Using the options argument, it is possible to combine different types of listeners:<br>
6406      * <br>
6407      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6408      * Code:<pre><code>
6409 el.on('click', this.onClick, this, {
6410     single: true,
6411     delay: 100,
6412     stopEvent : true,
6413     forumId: 4
6414 });</code></pre>
6415      * <p>
6416      * <b>Attaching multiple handlers in 1 call</b><br>
6417       * The method also allows for a single argument to be passed which is a config object containing properties
6418      * which specify multiple handlers.
6419      * <p>
6420      * Code:<pre><code>
6421 el.on({
6422     'click' : {
6423         fn: this.onClick
6424         scope: this,
6425         delay: 100
6426     },
6427     'mouseover' : {
6428         fn: this.onMouseOver
6429         scope: this
6430     },
6431     'mouseout' : {
6432         fn: this.onMouseOut
6433         scope: this
6434     }
6435 });</code></pre>
6436      * <p>
6437      * Or a shorthand syntax:<br>
6438      * Code:<pre><code>
6439 el.on({
6440     'click' : this.onClick,
6441     'mouseover' : this.onMouseOver,
6442     'mouseout' : this.onMouseOut
6443     scope: this
6444 });</code></pre>
6445      */
6446         addListener : function(element, eventName, fn, scope, options){
6447             if(typeof eventName == "object"){
6448                 var o = eventName;
6449                 for(var e in o){
6450                     if(propRe.test(e)){
6451                         continue;
6452                     }
6453                     if(typeof o[e] == "function"){
6454                         // shared options
6455                         listen(element, e, o, o[e], o.scope);
6456                     }else{
6457                         // individual options
6458                         listen(element, e, o[e]);
6459                     }
6460                 }
6461                 return;
6462             }
6463             return listen(element, eventName, options, fn, scope);
6464         },
6465         
6466         /**
6467          * Removes an event handler
6468          *
6469          * @param {String/HTMLElement}   element        The id or html element to remove the 
6470          *                             event from
6471          * @param {String}   eventName     The type of event
6472          * @param {Function} fn
6473          * @return {Boolean} True if a listener was actually removed
6474          */
6475         removeListener : function(element, eventName, fn){
6476             return stopListening(element, eventName, fn);
6477         },
6478         
6479         /**
6480          * Fires when the document is ready (before onload and before images are loaded). Can be 
6481          * accessed shorthanded Roo.onReady().
6482          * @param {Function} fn        The method the event invokes
6483          * @param {Object}   scope    An  object that becomes the scope of the handler
6484          * @param {boolean}  options
6485          */
6486         onDocumentReady : function(fn, scope, options){
6487             if(docReadyState){ // if it already fired
6488                 docReadyEvent.addListener(fn, scope, options);
6489                 docReadyEvent.fire();
6490                 docReadyEvent.clearListeners();
6491                 return;
6492             }
6493             if(!docReadyEvent){
6494                 initDocReady();
6495             }
6496             docReadyEvent.addListener(fn, scope, options);
6497         },
6498         
6499         /**
6500          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6501          * @param {Function} fn        The method the event invokes
6502          * @param {Object}   scope    An object that becomes the scope of the handler
6503          * @param {boolean}  options
6504          */
6505         onWindowResize : function(fn, scope, options){
6506             if(!resizeEvent){
6507                 resizeEvent = new Roo.util.Event();
6508                 resizeTask = new Roo.util.DelayedTask(function(){
6509                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6510                 });
6511                 E.on(window, "resize", function(){
6512                     if(Roo.isIE){
6513                         resizeTask.delay(50);
6514                     }else{
6515                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6516                     }
6517                 });
6518             }
6519             resizeEvent.addListener(fn, scope, options);
6520         },
6521
6522         /**
6523          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6524          * @param {Function} fn        The method the event invokes
6525          * @param {Object}   scope    An object that becomes the scope of the handler
6526          * @param {boolean}  options
6527          */
6528         onTextResize : function(fn, scope, options){
6529             if(!textEvent){
6530                 textEvent = new Roo.util.Event();
6531                 var textEl = new Roo.Element(document.createElement('div'));
6532                 textEl.dom.className = 'x-text-resize';
6533                 textEl.dom.innerHTML = 'X';
6534                 textEl.appendTo(document.body);
6535                 textSize = textEl.dom.offsetHeight;
6536                 setInterval(function(){
6537                     if(textEl.dom.offsetHeight != textSize){
6538                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6539                     }
6540                 }, this.textResizeInterval);
6541             }
6542             textEvent.addListener(fn, scope, options);
6543         },
6544
6545         /**
6546          * Removes the passed window resize listener.
6547          * @param {Function} fn        The method the event invokes
6548          * @param {Object}   scope    The scope of handler
6549          */
6550         removeResizeListener : function(fn, scope){
6551             if(resizeEvent){
6552                 resizeEvent.removeListener(fn, scope);
6553             }
6554         },
6555
6556         // private
6557         fireResize : function(){
6558             if(resizeEvent){
6559                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6560             }   
6561         },
6562         /**
6563          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6564          */
6565         ieDeferSrc : false,
6566         /**
6567          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6568          */
6569         textResizeInterval : 50
6570     };
6571     
6572     /**
6573      * Fix for doc tools
6574      * @scopeAlias pub=Roo.EventManager
6575      */
6576     
6577      /**
6578      * Appends an event handler to an element (shorthand for addListener)
6579      * @param {String/HTMLElement}   element        The html element or id to assign the
6580      * @param {String}   eventName The type of event to listen for
6581      * @param {Function} handler The method the event invokes
6582      * @param {Object}   scope (optional) The scope in which to execute the handler
6583      * function. The handler function's "this" context.
6584      * @param {Object}   options (optional) An object containing handler configuration
6585      * properties. This may contain any of the following properties:<ul>
6586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6589      * <li>preventDefault {Boolean} True to prevent the default action</li>
6590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6595      * by the specified number of milliseconds. If the event fires again within that time, the original
6596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6597      * </ul><br>
6598      * <p>
6599      * <b>Combining Options</b><br>
6600      * Using the options argument, it is possible to combine different types of listeners:<br>
6601      * <br>
6602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6603      * Code:<pre><code>
6604 el.on('click', this.onClick, this, {
6605     single: true,
6606     delay: 100,
6607     stopEvent : true,
6608     forumId: 4
6609 });</code></pre>
6610      * <p>
6611      * <b>Attaching multiple handlers in 1 call</b><br>
6612       * The method also allows for a single argument to be passed which is a config object containing properties
6613      * which specify multiple handlers.
6614      * <p>
6615      * Code:<pre><code>
6616 el.on({
6617     'click' : {
6618         fn: this.onClick
6619         scope: this,
6620         delay: 100
6621     },
6622     'mouseover' : {
6623         fn: this.onMouseOver
6624         scope: this
6625     },
6626     'mouseout' : {
6627         fn: this.onMouseOut
6628         scope: this
6629     }
6630 });</code></pre>
6631      * <p>
6632      * Or a shorthand syntax:<br>
6633      * Code:<pre><code>
6634 el.on({
6635     'click' : this.onClick,
6636     'mouseover' : this.onMouseOver,
6637     'mouseout' : this.onMouseOut
6638     scope: this
6639 });</code></pre>
6640      */
6641     pub.on = pub.addListener;
6642     pub.un = pub.removeListener;
6643
6644     pub.stoppedMouseDownEvent = new Roo.util.Event();
6645     return pub;
6646 }();
6647 /**
6648   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6649   * @param {Function} fn        The method the event invokes
6650   * @param {Object}   scope    An  object that becomes the scope of the handler
6651   * @param {boolean}  override If true, the obj passed in becomes
6652   *                             the execution scope of the listener
6653   * @member Roo
6654   * @method onReady
6655  */
6656 Roo.onReady = Roo.EventManager.onDocumentReady;
6657
6658 Roo.onReady(function(){
6659     var bd = Roo.get(document.body);
6660     if(!bd){ return; }
6661
6662     var cls = [
6663             Roo.isIE ? "roo-ie"
6664             : Roo.isIE11 ? "roo-ie11"
6665             : Roo.isEdge ? "roo-edge"
6666             : Roo.isGecko ? "roo-gecko"
6667             : Roo.isOpera ? "roo-opera"
6668             : Roo.isSafari ? "roo-safari" : ""];
6669
6670     if(Roo.isMac){
6671         cls.push("roo-mac");
6672     }
6673     if(Roo.isLinux){
6674         cls.push("roo-linux");
6675     }
6676     if(Roo.isIOS){
6677         cls.push("roo-ios");
6678     }
6679     if(Roo.isTouch){
6680         cls.push("roo-touch");
6681     }
6682     if(Roo.isBorderBox){
6683         cls.push('roo-border-box');
6684     }
6685     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6686         var p = bd.dom.parentNode;
6687         if(p){
6688             p.className += ' roo-strict';
6689         }
6690     }
6691     bd.addClass(cls.join(' '));
6692 });
6693
6694 /**
6695  * @class Roo.EventObject
6696  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6697  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6698  * Example:
6699  * <pre><code>
6700  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6701     e.preventDefault();
6702     var target = e.getTarget();
6703     ...
6704  }
6705  var myDiv = Roo.get("myDiv");
6706  myDiv.on("click", handleClick);
6707  //or
6708  Roo.EventManager.on("myDiv", 'click', handleClick);
6709  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6710  </code></pre>
6711  * @singleton
6712  */
6713 Roo.EventObject = function(){
6714     
6715     var E = Roo.lib.Event;
6716     
6717     // safari keypress events for special keys return bad keycodes
6718     var safariKeys = {
6719         63234 : 37, // left
6720         63235 : 39, // right
6721         63232 : 38, // up
6722         63233 : 40, // down
6723         63276 : 33, // page up
6724         63277 : 34, // page down
6725         63272 : 46, // delete
6726         63273 : 36, // home
6727         63275 : 35  // end
6728     };
6729
6730     // normalize button clicks
6731     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6732                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6733
6734     Roo.EventObjectImpl = function(e){
6735         if(e){
6736             this.setEvent(e.browserEvent || e);
6737         }
6738     };
6739     Roo.EventObjectImpl.prototype = {
6740         /**
6741          * Used to fix doc tools.
6742          * @scope Roo.EventObject.prototype
6743          */
6744             
6745
6746         
6747         
6748         /** The normal browser event */
6749         browserEvent : null,
6750         /** The button pressed in a mouse event */
6751         button : -1,
6752         /** True if the shift key was down during the event */
6753         shiftKey : false,
6754         /** True if the control key was down during the event */
6755         ctrlKey : false,
6756         /** True if the alt key was down during the event */
6757         altKey : false,
6758
6759         /** Key constant 
6760         * @type Number */
6761         BACKSPACE : 8,
6762         /** Key constant 
6763         * @type Number */
6764         TAB : 9,
6765         /** Key constant 
6766         * @type Number */
6767         RETURN : 13,
6768         /** Key constant 
6769         * @type Number */
6770         ENTER : 13,
6771         /** Key constant 
6772         * @type Number */
6773         SHIFT : 16,
6774         /** Key constant 
6775         * @type Number */
6776         CONTROL : 17,
6777         /** Key constant 
6778         * @type Number */
6779         ESC : 27,
6780         /** Key constant 
6781         * @type Number */
6782         SPACE : 32,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEUP : 33,
6786         /** Key constant 
6787         * @type Number */
6788         PAGEDOWN : 34,
6789         /** Key constant 
6790         * @type Number */
6791         END : 35,
6792         /** Key constant 
6793         * @type Number */
6794         HOME : 36,
6795         /** Key constant 
6796         * @type Number */
6797         LEFT : 37,
6798         /** Key constant 
6799         * @type Number */
6800         UP : 38,
6801         /** Key constant 
6802         * @type Number */
6803         RIGHT : 39,
6804         /** Key constant 
6805         * @type Number */
6806         DOWN : 40,
6807         /** Key constant 
6808         * @type Number */
6809         DELETE : 46,
6810         /** Key constant 
6811         * @type Number */
6812         F5 : 116,
6813
6814            /** @private */
6815         setEvent : function(e){
6816             if(e == this || (e && e.browserEvent)){ // already wrapped
6817                 return e;
6818             }
6819             this.browserEvent = e;
6820             if(e){
6821                 // normalize buttons
6822                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6823                 if(e.type == 'click' && this.button == -1){
6824                     this.button = 0;
6825                 }
6826                 this.type = e.type;
6827                 this.shiftKey = e.shiftKey;
6828                 // mac metaKey behaves like ctrlKey
6829                 this.ctrlKey = e.ctrlKey || e.metaKey;
6830                 this.altKey = e.altKey;
6831                 // in getKey these will be normalized for the mac
6832                 this.keyCode = e.keyCode;
6833                 // keyup warnings on firefox.
6834                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6835                 // cache the target for the delayed and or buffered events
6836                 this.target = E.getTarget(e);
6837                 // same for XY
6838                 this.xy = E.getXY(e);
6839             }else{
6840                 this.button = -1;
6841                 this.shiftKey = false;
6842                 this.ctrlKey = false;
6843                 this.altKey = false;
6844                 this.keyCode = 0;
6845                 this.charCode =0;
6846                 this.target = null;
6847                 this.xy = [0, 0];
6848             }
6849             return this;
6850         },
6851
6852         /**
6853          * Stop the event (preventDefault and stopPropagation)
6854          */
6855         stopEvent : function(){
6856             if(this.browserEvent){
6857                 if(this.browserEvent.type == 'mousedown'){
6858                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6859                 }
6860                 E.stopEvent(this.browserEvent);
6861             }
6862         },
6863
6864         /**
6865          * Prevents the browsers default handling of the event.
6866          */
6867         preventDefault : function(){
6868             if(this.browserEvent){
6869                 E.preventDefault(this.browserEvent);
6870             }
6871         },
6872
6873         /** @private */
6874         isNavKeyPress : function(){
6875             var k = this.keyCode;
6876             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6877             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6878         },
6879
6880         isSpecialKey : function(){
6881             var k = this.keyCode;
6882             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6883             (k == 16) || (k == 17) ||
6884             (k >= 18 && k <= 20) ||
6885             (k >= 33 && k <= 35) ||
6886             (k >= 36 && k <= 39) ||
6887             (k >= 44 && k <= 45);
6888         },
6889         /**
6890          * Cancels bubbling of the event.
6891          */
6892         stopPropagation : function(){
6893             if(this.browserEvent){
6894                 if(this.type == 'mousedown'){
6895                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6896                 }
6897                 E.stopPropagation(this.browserEvent);
6898             }
6899         },
6900
6901         /**
6902          * Gets the key code for the event.
6903          * @return {Number}
6904          */
6905         getCharCode : function(){
6906             return this.charCode || this.keyCode;
6907         },
6908
6909         /**
6910          * Returns a normalized keyCode for the event.
6911          * @return {Number} The key code
6912          */
6913         getKey : function(){
6914             var k = this.keyCode || this.charCode;
6915             return Roo.isSafari ? (safariKeys[k] || k) : k;
6916         },
6917
6918         /**
6919          * Gets the x coordinate of the event.
6920          * @return {Number}
6921          */
6922         getPageX : function(){
6923             return this.xy[0];
6924         },
6925
6926         /**
6927          * Gets the y coordinate of the event.
6928          * @return {Number}
6929          */
6930         getPageY : function(){
6931             return this.xy[1];
6932         },
6933
6934         /**
6935          * Gets the time of the event.
6936          * @return {Number}
6937          */
6938         getTime : function(){
6939             if(this.browserEvent){
6940                 return E.getTime(this.browserEvent);
6941             }
6942             return null;
6943         },
6944
6945         /**
6946          * Gets the page coordinates of the event.
6947          * @return {Array} The xy values like [x, y]
6948          */
6949         getXY : function(){
6950             return this.xy;
6951         },
6952
6953         /**
6954          * Gets the target for the event.
6955          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6956          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6957                 search as a number or element (defaults to 10 || document.body)
6958          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6959          * @return {HTMLelement}
6960          */
6961         getTarget : function(selector, maxDepth, returnEl){
6962             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6963         },
6964         /**
6965          * Gets the related target.
6966          * @return {HTMLElement}
6967          */
6968         getRelatedTarget : function(){
6969             if(this.browserEvent){
6970                 return E.getRelatedTarget(this.browserEvent);
6971             }
6972             return null;
6973         },
6974
6975         /**
6976          * Normalizes mouse wheel delta across browsers
6977          * @return {Number} The delta
6978          */
6979         getWheelDelta : function(){
6980             var e = this.browserEvent;
6981             var delta = 0;
6982             if(e.wheelDelta){ /* IE/Opera. */
6983                 delta = e.wheelDelta/120;
6984             }else if(e.detail){ /* Mozilla case. */
6985                 delta = -e.detail/3;
6986             }
6987             return delta;
6988         },
6989
6990         /**
6991          * Returns true if the control, meta, shift or alt key was pressed during this event.
6992          * @return {Boolean}
6993          */
6994         hasModifier : function(){
6995             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6996         },
6997
6998         /**
6999          * Returns true if the target of this event equals el or is a child of el
7000          * @param {String/HTMLElement/Element} el
7001          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7002          * @return {Boolean}
7003          */
7004         within : function(el, related){
7005             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7006             return t && Roo.fly(el).contains(t);
7007         },
7008
7009         getPoint : function(){
7010             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7011         }
7012     };
7013
7014     return new Roo.EventObjectImpl();
7015 }();
7016             
7017     /*
7018  * Based on:
7019  * Ext JS Library 1.1.1
7020  * Copyright(c) 2006-2007, Ext JS, LLC.
7021  *
7022  * Originally Released Under LGPL - original licence link has changed is not relivant.
7023  *
7024  * Fork - LGPL
7025  * <script type="text/javascript">
7026  */
7027
7028  
7029 // was in Composite Element!??!?!
7030  
7031 (function(){
7032     var D = Roo.lib.Dom;
7033     var E = Roo.lib.Event;
7034     var A = Roo.lib.Anim;
7035
7036     // local style camelizing for speed
7037     var propCache = {};
7038     var camelRe = /(-[a-z])/gi;
7039     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7040     var view = document.defaultView;
7041
7042 /**
7043  * @class Roo.Element
7044  * Represents an Element in the DOM.<br><br>
7045  * Usage:<br>
7046 <pre><code>
7047 var el = Roo.get("my-div");
7048
7049 // or with getEl
7050 var el = getEl("my-div");
7051
7052 // or with a DOM element
7053 var el = Roo.get(myDivElement);
7054 </code></pre>
7055  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7056  * each call instead of constructing a new one.<br><br>
7057  * <b>Animations</b><br />
7058  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7059  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7060 <pre>
7061 Option    Default   Description
7062 --------- --------  ---------------------------------------------
7063 duration  .35       The duration of the animation in seconds
7064 easing    easeOut   The YUI easing method
7065 callback  none      A function to execute when the anim completes
7066 scope     this      The scope (this) of the callback function
7067 </pre>
7068 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7069 * manipulate the animation. Here's an example:
7070 <pre><code>
7071 var el = Roo.get("my-div");
7072
7073 // no animation
7074 el.setWidth(100);
7075
7076 // default animation
7077 el.setWidth(100, true);
7078
7079 // animation with some options set
7080 el.setWidth(100, {
7081     duration: 1,
7082     callback: this.foo,
7083     scope: this
7084 });
7085
7086 // using the "anim" property to get the Anim object
7087 var opt = {
7088     duration: 1,
7089     callback: this.foo,
7090     scope: this
7091 };
7092 el.setWidth(100, opt);
7093 ...
7094 if(opt.anim.isAnimated()){
7095     opt.anim.stop();
7096 }
7097 </code></pre>
7098 * <b> Composite (Collections of) Elements</b><br />
7099  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7100  * @constructor Create a new Element directly.
7101  * @param {String/HTMLElement} element
7102  * @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).
7103  */
7104     Roo.Element = function(element, forceNew){
7105         var dom = typeof element == "string" ?
7106                 document.getElementById(element) : element;
7107         if(!dom){ // invalid id/element
7108             return null;
7109         }
7110         var id = dom.id;
7111         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7112             return Roo.Element.cache[id];
7113         }
7114
7115         /**
7116          * The DOM element
7117          * @type HTMLElement
7118          */
7119         this.dom = dom;
7120
7121         /**
7122          * The DOM element ID
7123          * @type String
7124          */
7125         this.id = id || Roo.id(dom);
7126     };
7127
7128     var El = Roo.Element;
7129
7130     El.prototype = {
7131         /**
7132          * The element's default display mode  (defaults to "")
7133          * @type String
7134          */
7135         originalDisplay : "",
7136
7137         visibilityMode : 1,
7138         /**
7139          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7140          * @type String
7141          */
7142         defaultUnit : "px",
7143         
7144         /**
7145          * Sets the element's visibility mode. When setVisible() is called it
7146          * will use this to determine whether to set the visibility or the display property.
7147          * @param visMode Element.VISIBILITY or Element.DISPLAY
7148          * @return {Roo.Element} this
7149          */
7150         setVisibilityMode : function(visMode){
7151             this.visibilityMode = visMode;
7152             return this;
7153         },
7154         /**
7155          * Convenience method for setVisibilityMode(Element.DISPLAY)
7156          * @param {String} display (optional) What to set display to when visible
7157          * @return {Roo.Element} this
7158          */
7159         enableDisplayMode : function(display){
7160             this.setVisibilityMode(El.DISPLAY);
7161             if(typeof display != "undefined") { this.originalDisplay = display; }
7162             return this;
7163         },
7164
7165         /**
7166          * 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)
7167          * @param {String} selector The simple selector to test
7168          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7169                 search as a number or element (defaults to 10 || document.body)
7170          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7171          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7172          */
7173         findParent : function(simpleSelector, maxDepth, returnEl){
7174             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7175             maxDepth = maxDepth || 50;
7176             if(typeof maxDepth != "number"){
7177                 stopEl = Roo.getDom(maxDepth);
7178                 maxDepth = 10;
7179             }
7180             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7181                 if(dq.is(p, simpleSelector)){
7182                     return returnEl ? Roo.get(p) : p;
7183                 }
7184                 depth++;
7185                 p = p.parentNode;
7186             }
7187             return null;
7188         },
7189
7190
7191         /**
7192          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParentNode : function(simpleSelector, maxDepth, returnEl){
7200             var p = Roo.fly(this.dom.parentNode, '_internal');
7201             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7202         },
7203         
7204         /**
7205          * Looks at  the scrollable parent element
7206          */
7207         findScrollableParent : function()
7208         {
7209             var overflowRegex = /(auto|scroll)/;
7210             
7211             if(this.getStyle('position') === 'fixed'){
7212                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7213             }
7214             
7215             var excludeStaticParent = this.getStyle('position') === "absolute";
7216             
7217             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7218                 
7219                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7220                     continue;
7221                 }
7222                 
7223                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7224                     return parent;
7225                 }
7226                 
7227                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7228                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7229                 }
7230             }
7231             
7232             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7233         },
7234
7235         /**
7236          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7237          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7242          */
7243         up : function(simpleSelector, maxDepth){
7244             return this.findParentNode(simpleSelector, maxDepth, true);
7245         },
7246
7247
7248
7249         /**
7250          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7251          * @param {String} selector The simple selector to test
7252          * @return {Boolean} True if this element matches the selector, else false
7253          */
7254         is : function(simpleSelector){
7255             return Roo.DomQuery.is(this.dom, simpleSelector);
7256         },
7257
7258         /**
7259          * Perform animation on this element.
7260          * @param {Object} args The YUI animation control args
7261          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7262          * @param {Function} onComplete (optional) Function to call when animation completes
7263          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7264          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7265          * @return {Roo.Element} this
7266          */
7267         animate : function(args, duration, onComplete, easing, animType){
7268             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7269             return this;
7270         },
7271
7272         /*
7273          * @private Internal animation call
7274          */
7275         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7276             animType = animType || 'run';
7277             opt = opt || {};
7278             var anim = Roo.lib.Anim[animType](
7279                 this.dom, args,
7280                 (opt.duration || defaultDur) || .35,
7281                 (opt.easing || defaultEase) || 'easeOut',
7282                 function(){
7283                     Roo.callback(cb, this);
7284                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7285                 },
7286                 this
7287             );
7288             opt.anim = anim;
7289             return anim;
7290         },
7291
7292         // private legacy anim prep
7293         preanim : function(a, i){
7294             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7295         },
7296
7297         /**
7298          * Removes worthless text nodes
7299          * @param {Boolean} forceReclean (optional) By default the element
7300          * keeps track if it has been cleaned already so
7301          * you can call this over and over. However, if you update the element and
7302          * need to force a reclean, you can pass true.
7303          */
7304         clean : function(forceReclean){
7305             if(this.isCleaned && forceReclean !== true){
7306                 return this;
7307             }
7308             var ns = /\S/;
7309             var d = this.dom, n = d.firstChild, ni = -1;
7310             while(n){
7311                 var nx = n.nextSibling;
7312                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7313                     d.removeChild(n);
7314                 }else{
7315                     n.nodeIndex = ++ni;
7316                 }
7317                 n = nx;
7318             }
7319             this.isCleaned = true;
7320             return this;
7321         },
7322
7323         // private
7324         calcOffsetsTo : function(el){
7325             el = Roo.get(el);
7326             var d = el.dom;
7327             var restorePos = false;
7328             if(el.getStyle('position') == 'static'){
7329                 el.position('relative');
7330                 restorePos = true;
7331             }
7332             var x = 0, y =0;
7333             var op = this.dom;
7334             while(op && op != d && op.tagName != 'HTML'){
7335                 x+= op.offsetLeft;
7336                 y+= op.offsetTop;
7337                 op = op.offsetParent;
7338             }
7339             if(restorePos){
7340                 el.position('static');
7341             }
7342             return [x, y];
7343         },
7344
7345         /**
7346          * Scrolls this element into view within the passed container.
7347          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7348          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7349          * @return {Roo.Element} this
7350          */
7351         scrollIntoView : function(container, hscroll){
7352             var c = Roo.getDom(container) || document.body;
7353             var el = this.dom;
7354
7355             var o = this.calcOffsetsTo(c),
7356                 l = o[0],
7357                 t = o[1],
7358                 b = t+el.offsetHeight,
7359                 r = l+el.offsetWidth;
7360
7361             var ch = c.clientHeight;
7362             var ct = parseInt(c.scrollTop, 10);
7363             var cl = parseInt(c.scrollLeft, 10);
7364             var cb = ct + ch;
7365             var cr = cl + c.clientWidth;
7366
7367             if(t < ct){
7368                 c.scrollTop = t;
7369             }else if(b > cb){
7370                 c.scrollTop = b-ch;
7371             }
7372
7373             if(hscroll !== false){
7374                 if(l < cl){
7375                     c.scrollLeft = l;
7376                 }else if(r > cr){
7377                     c.scrollLeft = r-c.clientWidth;
7378                 }
7379             }
7380             return this;
7381         },
7382
7383         // private
7384         scrollChildIntoView : function(child, hscroll){
7385             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7386         },
7387
7388         /**
7389          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7390          * the new height may not be available immediately.
7391          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7392          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7393          * @param {Function} onComplete (optional) Function to call when animation completes
7394          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7395          * @return {Roo.Element} this
7396          */
7397         autoHeight : function(animate, duration, onComplete, easing){
7398             var oldHeight = this.getHeight();
7399             this.clip();
7400             this.setHeight(1); // force clipping
7401             setTimeout(function(){
7402                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7403                 if(!animate){
7404                     this.setHeight(height);
7405                     this.unclip();
7406                     if(typeof onComplete == "function"){
7407                         onComplete();
7408                     }
7409                 }else{
7410                     this.setHeight(oldHeight); // restore original height
7411                     this.setHeight(height, animate, duration, function(){
7412                         this.unclip();
7413                         if(typeof onComplete == "function") { onComplete(); }
7414                     }.createDelegate(this), easing);
7415                 }
7416             }.createDelegate(this), 0);
7417             return this;
7418         },
7419
7420         /**
7421          * Returns true if this element is an ancestor of the passed element
7422          * @param {HTMLElement/String} el The element to check
7423          * @return {Boolean} True if this element is an ancestor of el, else false
7424          */
7425         contains : function(el){
7426             if(!el){return false;}
7427             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7428         },
7429
7430         /**
7431          * Checks whether the element is currently visible using both visibility and display properties.
7432          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7433          * @return {Boolean} True if the element is currently visible, else false
7434          */
7435         isVisible : function(deep) {
7436             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7437             if(deep !== true || !vis){
7438                 return vis;
7439             }
7440             var p = this.dom.parentNode;
7441             while(p && p.tagName.toLowerCase() != "body"){
7442                 if(!Roo.fly(p, '_isVisible').isVisible()){
7443                     return false;
7444                 }
7445                 p = p.parentNode;
7446             }
7447             return true;
7448         },
7449
7450         /**
7451          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7454          * @return {CompositeElement/CompositeElementLite} The composite element
7455          */
7456         select : function(selector, unique){
7457             return El.select(selector, unique, this.dom);
7458         },
7459
7460         /**
7461          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7462          * @param {String} selector The CSS selector
7463          * @return {Array} An array of the matched nodes
7464          */
7465         query : function(selector, unique){
7466             return Roo.DomQuery.select(selector, this.dom);
7467         },
7468
7469         /**
7470          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7471          * @param {String} selector The CSS selector
7472          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7473          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7474          */
7475         child : function(selector, returnDom){
7476             var n = Roo.DomQuery.selectNode(selector, this.dom);
7477             return returnDom ? n : Roo.get(n);
7478         },
7479
7480         /**
7481          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7482          * @param {String} selector The CSS selector
7483          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7484          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7485          */
7486         down : function(selector, returnDom){
7487             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7488             return returnDom ? n : Roo.get(n);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7493          * @param {String} group The group the DD object is member of
7494          * @param {Object} config The DD config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DD object
7496          * @return {Roo.dd.DD} The DD object
7497          */
7498         initDD : function(group, config, overrides){
7499             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7505          * @param {String} group The group the DDProxy object is member of
7506          * @param {Object} config The DDProxy config object
7507          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7508          * @return {Roo.dd.DDProxy} The DDProxy object
7509          */
7510         initDDProxy : function(group, config, overrides){
7511             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7512             return Roo.apply(dd, overrides);
7513         },
7514
7515         /**
7516          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7517          * @param {String} group The group the DDTarget object is member of
7518          * @param {Object} config The DDTarget config object
7519          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7520          * @return {Roo.dd.DDTarget} The DDTarget object
7521          */
7522         initDDTarget : function(group, config, overrides){
7523             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7524             return Roo.apply(dd, overrides);
7525         },
7526
7527         /**
7528          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7529          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7530          * @param {Boolean} visible Whether the element is visible
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534          setVisible : function(visible, animate){
7535             if(!animate || !A){
7536                 if(this.visibilityMode == El.DISPLAY){
7537                     this.setDisplayed(visible);
7538                 }else{
7539                     this.fixDisplay();
7540                     this.dom.style.visibility = visible ? "visible" : "hidden";
7541                 }
7542             }else{
7543                 // closure for composites
7544                 var dom = this.dom;
7545                 var visMode = this.visibilityMode;
7546                 if(visible){
7547                     this.setOpacity(.01);
7548                     this.setVisible(true);
7549                 }
7550                 this.anim({opacity: { to: (visible?1:0) }},
7551                       this.preanim(arguments, 1),
7552                       null, .35, 'easeIn', function(){
7553                          if(!visible){
7554                              if(visMode == El.DISPLAY){
7555                                  dom.style.display = "none";
7556                              }else{
7557                                  dom.style.visibility = "hidden";
7558                              }
7559                              Roo.get(dom).setOpacity(1);
7560                          }
7561                      });
7562             }
7563             return this;
7564         },
7565
7566         /**
7567          * Returns true if display is not "none"
7568          * @return {Boolean}
7569          */
7570         isDisplayed : function() {
7571             return this.getStyle("display") != "none";
7572         },
7573
7574         /**
7575          * Toggles the element's visibility or display, depending on visibility mode.
7576          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7577          * @return {Roo.Element} this
7578          */
7579         toggle : function(animate){
7580             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7581             return this;
7582         },
7583
7584         /**
7585          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7586          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7587          * @return {Roo.Element} this
7588          */
7589         setDisplayed : function(value) {
7590             if(typeof value == "boolean"){
7591                value = value ? this.originalDisplay : "none";
7592             }
7593             this.setStyle("display", value);
7594             return this;
7595         },
7596
7597         /**
7598          * Tries to focus the element. Any exceptions are caught and ignored.
7599          * @return {Roo.Element} this
7600          */
7601         focus : function() {
7602             try{
7603                 this.dom.focus();
7604             }catch(e){}
7605             return this;
7606         },
7607
7608         /**
7609          * Tries to blur the element. Any exceptions are caught and ignored.
7610          * @return {Roo.Element} this
7611          */
7612         blur : function() {
7613             try{
7614                 this.dom.blur();
7615             }catch(e){}
7616             return this;
7617         },
7618
7619         /**
7620          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7621          * @param {String/Array} className The CSS class to add, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         addClass : function(className){
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.addClass(className[i]);
7628                 }
7629             }else{
7630                 if(className && !this.hasClass(className)){
7631                     this.dom.className = this.dom.className + " " + className;
7632                 }
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7639          * @param {String/Array} className The CSS class to add, or an array of classes
7640          * @return {Roo.Element} this
7641          */
7642         radioClass : function(className){
7643             var siblings = this.dom.parentNode.childNodes;
7644             for(var i = 0; i < siblings.length; i++) {
7645                 var s = siblings[i];
7646                 if(s.nodeType == 1){
7647                     Roo.get(s).removeClass(className);
7648                 }
7649             }
7650             this.addClass(className);
7651             return this;
7652         },
7653
7654         /**
7655          * Removes one or more CSS classes from the element.
7656          * @param {String/Array} className The CSS class to remove, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         removeClass : function(className){
7660             if(!className || !this.dom.className){
7661                 return this;
7662             }
7663             if(className instanceof Array){
7664                 for(var i = 0, len = className.length; i < len; i++) {
7665                     this.removeClass(className[i]);
7666                 }
7667             }else{
7668                 if(this.hasClass(className)){
7669                     var re = this.classReCache[className];
7670                     if (!re) {
7671                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7672                        this.classReCache[className] = re;
7673                     }
7674                     this.dom.className =
7675                         this.dom.className.replace(re, " ");
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         // private
7682         classReCache: {},
7683
7684         /**
7685          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7686          * @param {String} className The CSS class to toggle
7687          * @return {Roo.Element} this
7688          */
7689         toggleClass : function(className){
7690             if(this.hasClass(className)){
7691                 this.removeClass(className);
7692             }else{
7693                 this.addClass(className);
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Checks if the specified CSS class exists on this element's DOM node.
7700          * @param {String} className The CSS class to check for
7701          * @return {Boolean} True if the class exists, else false
7702          */
7703         hasClass : function(className){
7704             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7705         },
7706
7707         /**
7708          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7709          * @param {String} oldClassName The CSS class to replace
7710          * @param {String} newClassName The replacement CSS class
7711          * @return {Roo.Element} this
7712          */
7713         replaceClass : function(oldClassName, newClassName){
7714             this.removeClass(oldClassName);
7715             this.addClass(newClassName);
7716             return this;
7717         },
7718
7719         /**
7720          * Returns an object with properties matching the styles requested.
7721          * For example, el.getStyles('color', 'font-size', 'width') might return
7722          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7723          * @param {String} style1 A style name
7724          * @param {String} style2 A style name
7725          * @param {String} etc.
7726          * @return {Object} The style object
7727          */
7728         getStyles : function(){
7729             var a = arguments, len = a.length, r = {};
7730             for(var i = 0; i < len; i++){
7731                 r[a[i]] = this.getStyle(a[i]);
7732             }
7733             return r;
7734         },
7735
7736         /**
7737          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7738          * @param {String} property The style property whose value is returned.
7739          * @return {String} The current value of the style property for this element.
7740          */
7741         getStyle : function(){
7742             return view && view.getComputedStyle ?
7743                 function(prop){
7744                     var el = this.dom, v, cs, camel;
7745                     if(prop == 'float'){
7746                         prop = "cssFloat";
7747                     }
7748                     if(el.style && (v = el.style[prop])){
7749                         return v;
7750                     }
7751                     if(cs = view.getComputedStyle(el, "")){
7752                         if(!(camel = propCache[prop])){
7753                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                         }
7755                         return cs[camel];
7756                     }
7757                     return null;
7758                 } :
7759                 function(prop){
7760                     var el = this.dom, v, cs, camel;
7761                     if(prop == 'opacity'){
7762                         if(typeof el.style.filter == 'string'){
7763                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7764                             if(m){
7765                                 var fv = parseFloat(m[1]);
7766                                 if(!isNaN(fv)){
7767                                     return fv ? fv / 100 : 0;
7768                                 }
7769                             }
7770                         }
7771                         return 1;
7772                     }else if(prop == 'float'){
7773                         prop = "styleFloat";
7774                     }
7775                     if(!(camel = propCache[prop])){
7776                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7777                     }
7778                     if(v = el.style[camel]){
7779                         return v;
7780                     }
7781                     if(cs = el.currentStyle){
7782                         return cs[camel];
7783                     }
7784                     return null;
7785                 };
7786         }(),
7787
7788         /**
7789          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7790          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7791          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7792          * @return {Roo.Element} this
7793          */
7794         setStyle : function(prop, value){
7795             if(typeof prop == "string"){
7796                 
7797                 if (prop == 'float') {
7798                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7799                     return this;
7800                 }
7801                 
7802                 var camel;
7803                 if(!(camel = propCache[prop])){
7804                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7805                 }
7806                 
7807                 if(camel == 'opacity') {
7808                     this.setOpacity(value);
7809                 }else{
7810                     this.dom.style[camel] = value;
7811                 }
7812             }else{
7813                 for(var style in prop){
7814                     if(typeof prop[style] != "function"){
7815                        this.setStyle(style, prop[style]);
7816                     }
7817                 }
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * More flexible version of {@link #setStyle} for setting style properties.
7824          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7825          * a function which returns such a specification.
7826          * @return {Roo.Element} this
7827          */
7828         applyStyles : function(style){
7829             Roo.DomHelper.applyStyles(this.dom, style);
7830             return this;
7831         },
7832
7833         /**
7834           * 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).
7835           * @return {Number} The X position of the element
7836           */
7837         getX : function(){
7838             return D.getX(this.dom);
7839         },
7840
7841         /**
7842           * 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).
7843           * @return {Number} The Y position of the element
7844           */
7845         getY : function(){
7846             return D.getY(this.dom);
7847         },
7848
7849         /**
7850           * 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).
7851           * @return {Array} The XY position of the element
7852           */
7853         getXY : function(){
7854             return D.getXY(this.dom);
7855         },
7856
7857         /**
7858          * 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).
7859          * @param {Number} The X position of the element
7860          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7861          * @return {Roo.Element} this
7862          */
7863         setX : function(x, animate){
7864             if(!animate || !A){
7865                 D.setX(this.dom, x);
7866             }else{
7867                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7868             }
7869             return this;
7870         },
7871
7872         /**
7873          * 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).
7874          * @param {Number} The Y position of the element
7875          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7876          * @return {Roo.Element} this
7877          */
7878         setY : function(y, animate){
7879             if(!animate || !A){
7880                 D.setY(this.dom, y);
7881             }else{
7882                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7889          * @param {String} left The left CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setLeft : function(left){
7893             this.setStyle("left", this.addUnits(left));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7899          * @param {String} top The top CSS property value
7900          * @return {Roo.Element} this
7901          */
7902         setTop : function(top){
7903             this.setStyle("top", this.addUnits(top));
7904             return this;
7905         },
7906
7907         /**
7908          * Sets the element's CSS right style.
7909          * @param {String} right The right CSS property value
7910          * @return {Roo.Element} this
7911          */
7912         setRight : function(right){
7913             this.setStyle("right", this.addUnits(right));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the element's CSS bottom style.
7919          * @param {String} bottom The bottom CSS property value
7920          * @return {Roo.Element} this
7921          */
7922         setBottom : function(bottom){
7923             this.setStyle("bottom", this.addUnits(bottom));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         setXY : function(pos, animate){
7935             if(!animate || !A){
7936                 D.setXY(this.dom, pos);
7937             }else{
7938                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7945          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7949          * @return {Roo.Element} this
7950          */
7951         setLocation : function(x, y, animate){
7952             this.setXY([x, y], this.preanim(arguments, 2));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7958          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7962          * @return {Roo.Element} this
7963          */
7964         moveTo : function(x, y, animate){
7965             this.setXY([x, y], this.preanim(arguments, 2));
7966             return this;
7967         },
7968
7969         /**
7970          * Returns the region of the given element.
7971          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7972          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7973          */
7974         getRegion : function(){
7975             return D.getRegion(this.dom);
7976         },
7977
7978         /**
7979          * Returns the offset height of the element
7980          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7981          * @return {Number} The element's height
7982          */
7983         getHeight : function(contentHeight){
7984             var h = this.dom.offsetHeight || 0;
7985             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7986         },
7987
7988         /**
7989          * Returns the offset width of the element
7990          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7991          * @return {Number} The element's width
7992          */
7993         getWidth : function(contentWidth){
7994             var w = this.dom.offsetWidth || 0;
7995             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7996         },
7997
7998         /**
7999          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8000          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8001          * if a height has not been set using CSS.
8002          * @return {Number}
8003          */
8004         getComputedHeight : function(){
8005             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8006             if(!h){
8007                 h = parseInt(this.getStyle('height'), 10) || 0;
8008                 if(!this.isBorderBox()){
8009                     h += this.getFrameWidth('tb');
8010                 }
8011             }
8012             return h;
8013         },
8014
8015         /**
8016          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8017          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8018          * if a width has not been set using CSS.
8019          * @return {Number}
8020          */
8021         getComputedWidth : function(){
8022             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8023             if(!w){
8024                 w = parseInt(this.getStyle('width'), 10) || 0;
8025                 if(!this.isBorderBox()){
8026                     w += this.getFrameWidth('lr');
8027                 }
8028             }
8029             return w;
8030         },
8031
8032         /**
8033          * Returns the size of the element.
8034          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8035          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8036          */
8037         getSize : function(contentSize){
8038             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8039         },
8040
8041         /**
8042          * Returns the width and height of the viewport.
8043          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8044          */
8045         getViewSize : function(){
8046             var d = this.dom, doc = document, aw = 0, ah = 0;
8047             if(d == doc || d == doc.body){
8048                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8049             }else{
8050                 return {
8051                     width : d.clientWidth,
8052                     height: d.clientHeight
8053                 };
8054             }
8055         },
8056
8057         /**
8058          * Returns the value of the "value" attribute
8059          * @param {Boolean} asNumber true to parse the value as a number
8060          * @return {String/Number}
8061          */
8062         getValue : function(asNumber){
8063             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8064         },
8065
8066         // private
8067         adjustWidth : function(width){
8068             if(typeof width == "number"){
8069                 if(this.autoBoxAdjust && !this.isBorderBox()){
8070                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8071                 }
8072                 if(width < 0){
8073                     width = 0;
8074                 }
8075             }
8076             return width;
8077         },
8078
8079         // private
8080         adjustHeight : function(height){
8081             if(typeof height == "number"){
8082                if(this.autoBoxAdjust && !this.isBorderBox()){
8083                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8084                }
8085                if(height < 0){
8086                    height = 0;
8087                }
8088             }
8089             return height;
8090         },
8091
8092         /**
8093          * Set the width of the element
8094          * @param {Number} width The new width
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098         setWidth : function(width, animate){
8099             width = this.adjustWidth(width);
8100             if(!animate || !A){
8101                 this.dom.style.width = this.addUnits(width);
8102             }else{
8103                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8104             }
8105             return this;
8106         },
8107
8108         /**
8109          * Set the height of the element
8110          * @param {Number} height The new height
8111          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8112          * @return {Roo.Element} this
8113          */
8114          setHeight : function(height, animate){
8115             height = this.adjustHeight(height);
8116             if(!animate || !A){
8117                 this.dom.style.height = this.addUnits(height);
8118             }else{
8119                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131          setSize : function(width, height, animate){
8132             if(typeof width == "object"){ // in case of object from getSize()
8133                 height = width.height; width = width.width;
8134             }
8135             width = this.adjustWidth(width); height = this.adjustHeight(height);
8136             if(!animate || !A){
8137                 this.dom.style.width = this.addUnits(width);
8138                 this.dom.style.height = this.addUnits(height);
8139             }else{
8140                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8141             }
8142             return this;
8143         },
8144
8145         /**
8146          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8147          * @param {Number} x X value for new position (coordinates are page-based)
8148          * @param {Number} y Y value for new position (coordinates are page-based)
8149          * @param {Number} width The new width
8150          * @param {Number} height The new height
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154         setBounds : function(x, y, width, height, animate){
8155             if(!animate || !A){
8156                 this.setSize(width, height);
8157                 this.setLocation(x, y);
8158             }else{
8159                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8160                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8161                               this.preanim(arguments, 4), 'motion');
8162             }
8163             return this;
8164         },
8165
8166         /**
8167          * 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.
8168          * @param {Roo.lib.Region} region The region to fill
8169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8170          * @return {Roo.Element} this
8171          */
8172         setRegion : function(region, animate){
8173             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8174             return this;
8175         },
8176
8177         /**
8178          * Appends an event handler
8179          *
8180          * @param {String}   eventName     The type of event to append
8181          * @param {Function} fn        The method the event invokes
8182          * @param {Object} scope       (optional) The scope (this object) of the fn
8183          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8184          */
8185         addListener : function(eventName, fn, scope, options){
8186             if (this.dom) {
8187                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8188             }
8189         },
8190
8191         /**
8192          * Removes an event handler from this element
8193          * @param {String} eventName the type of event to remove
8194          * @param {Function} fn the method the event invokes
8195          * @return {Roo.Element} this
8196          */
8197         removeListener : function(eventName, fn){
8198             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8199             return this;
8200         },
8201
8202         /**
8203          * Removes all previous added listeners from this element
8204          * @return {Roo.Element} this
8205          */
8206         removeAllListeners : function(){
8207             E.purgeElement(this.dom);
8208             return this;
8209         },
8210
8211         relayEvent : function(eventName, observable){
8212             this.on(eventName, function(e){
8213                 observable.fireEvent(eventName, e);
8214             });
8215         },
8216
8217         /**
8218          * Set the opacity of the element
8219          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8220          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8221          * @return {Roo.Element} this
8222          */
8223          setOpacity : function(opacity, animate){
8224             if(!animate || !A){
8225                 var s = this.dom.style;
8226                 if(Roo.isIE){
8227                     s.zoom = 1;
8228                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8229                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8230                 }else{
8231                     s.opacity = opacity;
8232                 }
8233             }else{
8234                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8235             }
8236             return this;
8237         },
8238
8239         /**
8240          * Gets the left X coordinate
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getLeft : function(local){
8245             if(!local){
8246                 return this.getX();
8247             }else{
8248                 return parseInt(this.getStyle("left"), 10) || 0;
8249             }
8250         },
8251
8252         /**
8253          * Gets the right X coordinate of the element (element X position + element width)
8254          * @param {Boolean} local True to get the local css position instead of page coordinate
8255          * @return {Number}
8256          */
8257         getRight : function(local){
8258             if(!local){
8259                 return this.getX() + this.getWidth();
8260             }else{
8261                 return (this.getLeft(true) + this.getWidth()) || 0;
8262             }
8263         },
8264
8265         /**
8266          * Gets the top Y coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getTop : function(local) {
8271             if(!local){
8272                 return this.getY();
8273             }else{
8274                 return parseInt(this.getStyle("top"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the bottom Y coordinate of the element (element Y position + element height)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getBottom : function(local){
8284             if(!local){
8285                 return this.getY() + this.getHeight();
8286             }else{
8287                 return (this.getTop(true) + this.getHeight()) || 0;
8288             }
8289         },
8290
8291         /**
8292         * Initializes positioning on this element. If a desired position is not passed, it will make the
8293         * the element positioned relative IF it is not already positioned.
8294         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8295         * @param {Number} zIndex (optional) The zIndex to apply
8296         * @param {Number} x (optional) Set the page X position
8297         * @param {Number} y (optional) Set the page Y position
8298         */
8299         position : function(pos, zIndex, x, y){
8300             if(!pos){
8301                if(this.getStyle('position') == 'static'){
8302                    this.setStyle('position', 'relative');
8303                }
8304             }else{
8305                 this.setStyle("position", pos);
8306             }
8307             if(zIndex){
8308                 this.setStyle("z-index", zIndex);
8309             }
8310             if(x !== undefined && y !== undefined){
8311                 this.setXY([x, y]);
8312             }else if(x !== undefined){
8313                 this.setX(x);
8314             }else if(y !== undefined){
8315                 this.setY(y);
8316             }
8317         },
8318
8319         /**
8320         * Clear positioning back to the default when the document was loaded
8321         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8322         * @return {Roo.Element} this
8323          */
8324         clearPositioning : function(value){
8325             value = value ||'';
8326             this.setStyle({
8327                 "left": value,
8328                 "right": value,
8329                 "top": value,
8330                 "bottom": value,
8331                 "z-index": "",
8332                 "position" : "static"
8333             });
8334             return this;
8335         },
8336
8337         /**
8338         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8339         * snapshot before performing an update and then restoring the element.
8340         * @return {Object}
8341         */
8342         getPositioning : function(){
8343             var l = this.getStyle("left");
8344             var t = this.getStyle("top");
8345             return {
8346                 "position" : this.getStyle("position"),
8347                 "left" : l,
8348                 "right" : l ? "" : this.getStyle("right"),
8349                 "top" : t,
8350                 "bottom" : t ? "" : this.getStyle("bottom"),
8351                 "z-index" : this.getStyle("z-index")
8352             };
8353         },
8354
8355         /**
8356          * Gets the width of the border(s) for the specified side(s)
8357          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8358          * passing lr would get the border (l)eft width + the border (r)ight width.
8359          * @return {Number} The width of the sides passed added together
8360          */
8361         getBorderWidth : function(side){
8362             return this.addStyles(side, El.borders);
8363         },
8364
8365         /**
8366          * Gets the width of the padding(s) for the specified side(s)
8367          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8368          * passing lr would get the padding (l)eft + the padding (r)ight.
8369          * @return {Number} The padding of the sides passed added together
8370          */
8371         getPadding : function(side){
8372             return this.addStyles(side, El.paddings);
8373         },
8374
8375         /**
8376         * Set positioning with an object returned by getPositioning().
8377         * @param {Object} posCfg
8378         * @return {Roo.Element} this
8379          */
8380         setPositioning : function(pc){
8381             this.applyStyles(pc);
8382             if(pc.right == "auto"){
8383                 this.dom.style.right = "";
8384             }
8385             if(pc.bottom == "auto"){
8386                 this.dom.style.bottom = "";
8387             }
8388             return this;
8389         },
8390
8391         // private
8392         fixDisplay : function(){
8393             if(this.getStyle("display") == "none"){
8394                 this.setStyle("visibility", "hidden");
8395                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8396                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8397                     this.setStyle("display", "block");
8398                 }
8399             }
8400         },
8401
8402         /**
8403          * Quick set left and top adding default units
8404          * @param {String} left The left CSS property value
8405          * @param {String} top The top CSS property value
8406          * @return {Roo.Element} this
8407          */
8408          setLeftTop : function(left, top){
8409             this.dom.style.left = this.addUnits(left);
8410             this.dom.style.top = this.addUnits(top);
8411             return this;
8412         },
8413
8414         /**
8415          * Move this element relative to its current position.
8416          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8417          * @param {Number} distance How far to move the element in pixels
8418          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8419          * @return {Roo.Element} this
8420          */
8421          move : function(direction, distance, animate){
8422             var xy = this.getXY();
8423             direction = direction.toLowerCase();
8424             switch(direction){
8425                 case "l":
8426                 case "left":
8427                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8428                     break;
8429                case "r":
8430                case "right":
8431                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8432                     break;
8433                case "t":
8434                case "top":
8435                case "up":
8436                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8437                     break;
8438                case "b":
8439                case "bottom":
8440                case "down":
8441                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8442                     break;
8443             }
8444             return this;
8445         },
8446
8447         /**
8448          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8449          * @return {Roo.Element} this
8450          */
8451         clip : function(){
8452             if(!this.isClipped){
8453                this.isClipped = true;
8454                this.originalClip = {
8455                    "o": this.getStyle("overflow"),
8456                    "x": this.getStyle("overflow-x"),
8457                    "y": this.getStyle("overflow-y")
8458                };
8459                this.setStyle("overflow", "hidden");
8460                this.setStyle("overflow-x", "hidden");
8461                this.setStyle("overflow-y", "hidden");
8462             }
8463             return this;
8464         },
8465
8466         /**
8467          *  Return clipping (overflow) to original clipping before clip() was called
8468          * @return {Roo.Element} this
8469          */
8470         unclip : function(){
8471             if(this.isClipped){
8472                 this.isClipped = false;
8473                 var o = this.originalClip;
8474                 if(o.o){this.setStyle("overflow", o.o);}
8475                 if(o.x){this.setStyle("overflow-x", o.x);}
8476                 if(o.y){this.setStyle("overflow-y", o.y);}
8477             }
8478             return this;
8479         },
8480
8481
8482         /**
8483          * Gets the x,y coordinates specified by the anchor position on the element.
8484          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8485          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8486          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8487          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8488          * @return {Array} [x, y] An array containing the element's x and y coordinates
8489          */
8490         getAnchorXY : function(anchor, local, s){
8491             //Passing a different size is useful for pre-calculating anchors,
8492             //especially for anchored animations that change the el size.
8493
8494             var w, h, vp = false;
8495             if(!s){
8496                 var d = this.dom;
8497                 if(d == document.body || d == document){
8498                     vp = true;
8499                     w = D.getViewWidth(); h = D.getViewHeight();
8500                 }else{
8501                     w = this.getWidth(); h = this.getHeight();
8502                 }
8503             }else{
8504                 w = s.width;  h = s.height;
8505             }
8506             var x = 0, y = 0, r = Math.round;
8507             switch((anchor || "tl").toLowerCase()){
8508                 case "c":
8509                     x = r(w*.5);
8510                     y = r(h*.5);
8511                 break;
8512                 case "t":
8513                     x = r(w*.5);
8514                     y = 0;
8515                 break;
8516                 case "l":
8517                     x = 0;
8518                     y = r(h*.5);
8519                 break;
8520                 case "r":
8521                     x = w;
8522                     y = r(h*.5);
8523                 break;
8524                 case "b":
8525                     x = r(w*.5);
8526                     y = h;
8527                 break;
8528                 case "tl":
8529                     x = 0;
8530                     y = 0;
8531                 break;
8532                 case "bl":
8533                     x = 0;
8534                     y = h;
8535                 break;
8536                 case "br":
8537                     x = w;
8538                     y = h;
8539                 break;
8540                 case "tr":
8541                     x = w;
8542                     y = 0;
8543                 break;
8544             }
8545             if(local === true){
8546                 return [x, y];
8547             }
8548             if(vp){
8549                 var sc = this.getScroll();
8550                 return [x + sc.left, y + sc.top];
8551             }
8552             //Add the element's offset xy
8553             var o = this.getXY();
8554             return [x+o[0], y+o[1]];
8555         },
8556
8557         /**
8558          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8559          * supported position values.
8560          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8561          * @param {String} position The position to align to.
8562          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8563          * @return {Array} [x, y]
8564          */
8565         getAlignToXY : function(el, p, o){
8566             el = Roo.get(el);
8567             var d = this.dom;
8568             if(!el.dom){
8569                 throw "Element.alignTo with an element that doesn't exist";
8570             }
8571             var c = false; //constrain to viewport
8572             var p1 = "", p2 = "";
8573             o = o || [0,0];
8574
8575             if(!p){
8576                 p = "tl-bl";
8577             }else if(p == "?"){
8578                 p = "tl-bl?";
8579             }else if(p.indexOf("-") == -1){
8580                 p = "tl-" + p;
8581             }
8582             p = p.toLowerCase();
8583             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8584             if(!m){
8585                throw "Element.alignTo with an invalid alignment " + p;
8586             }
8587             p1 = m[1]; p2 = m[2]; c = !!m[3];
8588
8589             //Subtract the aligned el's internal xy from the target's offset xy
8590             //plus custom offset to get the aligned el's new offset xy
8591             var a1 = this.getAnchorXY(p1, true);
8592             var a2 = el.getAnchorXY(p2, false);
8593             var x = a2[0] - a1[0] + o[0];
8594             var y = a2[1] - a1[1] + o[1];
8595             if(c){
8596                 //constrain the aligned el to viewport if necessary
8597                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8598                 // 5px of margin for ie
8599                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8600
8601                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8602                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8603                 //otherwise swap the aligned el to the opposite border of the target.
8604                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8605                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8606                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8607                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8608
8609                var doc = document;
8610                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8611                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8612
8613                if((x+w) > dw + scrollX){
8614                     x = swapX ? r.left-w : dw+scrollX-w;
8615                 }
8616                if(x < scrollX){
8617                    x = swapX ? r.right : scrollX;
8618                }
8619                if((y+h) > dh + scrollY){
8620                     y = swapY ? r.top-h : dh+scrollY-h;
8621                 }
8622                if (y < scrollY){
8623                    y = swapY ? r.bottom : scrollY;
8624                }
8625             }
8626             return [x,y];
8627         },
8628
8629         // private
8630         getConstrainToXY : function(){
8631             var os = {top:0, left:0, bottom:0, right: 0};
8632
8633             return function(el, local, offsets, proposedXY){
8634                 el = Roo.get(el);
8635                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8636
8637                 var vw, vh, vx = 0, vy = 0;
8638                 if(el.dom == document.body || el.dom == document){
8639                     vw = Roo.lib.Dom.getViewWidth();
8640                     vh = Roo.lib.Dom.getViewHeight();
8641                 }else{
8642                     vw = el.dom.clientWidth;
8643                     vh = el.dom.clientHeight;
8644                     if(!local){
8645                         var vxy = el.getXY();
8646                         vx = vxy[0];
8647                         vy = vxy[1];
8648                     }
8649                 }
8650
8651                 var s = el.getScroll();
8652
8653                 vx += offsets.left + s.left;
8654                 vy += offsets.top + s.top;
8655
8656                 vw -= offsets.right;
8657                 vh -= offsets.bottom;
8658
8659                 var vr = vx+vw;
8660                 var vb = vy+vh;
8661
8662                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8663                 var x = xy[0], y = xy[1];
8664                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8665
8666                 // only move it if it needs it
8667                 var moved = false;
8668
8669                 // first validate right/bottom
8670                 if((x + w) > vr){
8671                     x = vr - w;
8672                     moved = true;
8673                 }
8674                 if((y + h) > vb){
8675                     y = vb - h;
8676                     moved = true;
8677                 }
8678                 // then make sure top/left isn't negative
8679                 if(x < vx){
8680                     x = vx;
8681                     moved = true;
8682                 }
8683                 if(y < vy){
8684                     y = vy;
8685                     moved = true;
8686                 }
8687                 return moved ? [x, y] : false;
8688             };
8689         }(),
8690
8691         // private
8692         adjustForConstraints : function(xy, parent, offsets){
8693             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8694         },
8695
8696         /**
8697          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8698          * document it aligns it to the viewport.
8699          * The position parameter is optional, and can be specified in any one of the following formats:
8700          * <ul>
8701          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8702          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8703          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8704          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8705          *   <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
8706          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8707          * </ul>
8708          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8709          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8710          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8711          * that specified in order to enforce the viewport constraints.
8712          * Following are all of the supported anchor positions:
8713     <pre>
8714     Value  Description
8715     -----  -----------------------------
8716     tl     The top left corner (default)
8717     t      The center of the top edge
8718     tr     The top right corner
8719     l      The center of the left edge
8720     c      In the center of the element
8721     r      The center of the right edge
8722     bl     The bottom left corner
8723     b      The center of the bottom edge
8724     br     The bottom right corner
8725     </pre>
8726     Example Usage:
8727     <pre><code>
8728     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8729     el.alignTo("other-el");
8730
8731     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8732     el.alignTo("other-el", "tr?");
8733
8734     // align the bottom right corner of el with the center left edge of other-el
8735     el.alignTo("other-el", "br-l?");
8736
8737     // align the center of el with the bottom left corner of other-el and
8738     // adjust the x position by -6 pixels (and the y position by 0)
8739     el.alignTo("other-el", "c-bl", [-6, 0]);
8740     </code></pre>
8741          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8742          * @param {String} position The position to align to.
8743          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8745          * @return {Roo.Element} this
8746          */
8747         alignTo : function(element, position, offsets, animate){
8748             var xy = this.getAlignToXY(element, position, offsets);
8749             this.setXY(xy, this.preanim(arguments, 3));
8750             return this;
8751         },
8752
8753         /**
8754          * Anchors an element to another element and realigns it when the window is resized.
8755          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8756          * @param {String} position The position to align to.
8757          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8758          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8759          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8760          * is a number, it is used as the buffer delay (defaults to 50ms).
8761          * @param {Function} callback The function to call after the animation finishes
8762          * @return {Roo.Element} this
8763          */
8764         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8765             var action = function(){
8766                 this.alignTo(el, alignment, offsets, animate);
8767                 Roo.callback(callback, this);
8768             };
8769             Roo.EventManager.onWindowResize(action, this);
8770             var tm = typeof monitorScroll;
8771             if(tm != 'undefined'){
8772                 Roo.EventManager.on(window, 'scroll', action, this,
8773                     {buffer: tm == 'number' ? monitorScroll : 50});
8774             }
8775             action.call(this); // align immediately
8776             return this;
8777         },
8778         /**
8779          * Clears any opacity settings from this element. Required in some cases for IE.
8780          * @return {Roo.Element} this
8781          */
8782         clearOpacity : function(){
8783             if (window.ActiveXObject) {
8784                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8785                     this.dom.style.filter = "";
8786                 }
8787             } else {
8788                 this.dom.style.opacity = "";
8789                 this.dom.style["-moz-opacity"] = "";
8790                 this.dom.style["-khtml-opacity"] = "";
8791             }
8792             return this;
8793         },
8794
8795         /**
8796          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8797          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8798          * @return {Roo.Element} this
8799          */
8800         hide : function(animate){
8801             this.setVisible(false, this.preanim(arguments, 0));
8802             return this;
8803         },
8804
8805         /**
8806         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8807         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8808          * @return {Roo.Element} this
8809          */
8810         show : function(animate){
8811             this.setVisible(true, this.preanim(arguments, 0));
8812             return this;
8813         },
8814
8815         /**
8816          * @private Test if size has a unit, otherwise appends the default
8817          */
8818         addUnits : function(size){
8819             return Roo.Element.addUnits(size, this.defaultUnit);
8820         },
8821
8822         /**
8823          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8824          * @return {Roo.Element} this
8825          */
8826         beginMeasure : function(){
8827             var el = this.dom;
8828             if(el.offsetWidth || el.offsetHeight){
8829                 return this; // offsets work already
8830             }
8831             var changed = [];
8832             var p = this.dom, b = document.body; // start with this element
8833             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8834                 var pe = Roo.get(p);
8835                 if(pe.getStyle('display') == 'none'){
8836                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8837                     p.style.visibility = "hidden";
8838                     p.style.display = "block";
8839                 }
8840                 p = p.parentNode;
8841             }
8842             this._measureChanged = changed;
8843             return this;
8844
8845         },
8846
8847         /**
8848          * Restores displays to before beginMeasure was called
8849          * @return {Roo.Element} this
8850          */
8851         endMeasure : function(){
8852             var changed = this._measureChanged;
8853             if(changed){
8854                 for(var i = 0, len = changed.length; i < len; i++) {
8855                     var r = changed[i];
8856                     r.el.style.visibility = r.visibility;
8857                     r.el.style.display = "none";
8858                 }
8859                 this._measureChanged = null;
8860             }
8861             return this;
8862         },
8863
8864         /**
8865         * Update the innerHTML of this element, optionally searching for and processing scripts
8866         * @param {String} html The new HTML
8867         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8868         * @param {Function} callback For async script loading you can be noticed when the update completes
8869         * @return {Roo.Element} this
8870          */
8871         update : function(html, loadScripts, callback){
8872             if(typeof html == "undefined"){
8873                 html = "";
8874             }
8875             if(loadScripts !== true){
8876                 this.dom.innerHTML = html;
8877                 if(typeof callback == "function"){
8878                     callback();
8879                 }
8880                 return this;
8881             }
8882             var id = Roo.id();
8883             var dom = this.dom;
8884
8885             html += '<span id="' + id + '"></span>';
8886
8887             E.onAvailable(id, function(){
8888                 var hd = document.getElementsByTagName("head")[0];
8889                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8890                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8891                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8892
8893                 var match;
8894                 while(match = re.exec(html)){
8895                     var attrs = match[1];
8896                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8897                     if(srcMatch && srcMatch[2]){
8898                        var s = document.createElement("script");
8899                        s.src = srcMatch[2];
8900                        var typeMatch = attrs.match(typeRe);
8901                        if(typeMatch && typeMatch[2]){
8902                            s.type = typeMatch[2];
8903                        }
8904                        hd.appendChild(s);
8905                     }else if(match[2] && match[2].length > 0){
8906                         if(window.execScript) {
8907                            window.execScript(match[2]);
8908                         } else {
8909                             /**
8910                              * eval:var:id
8911                              * eval:var:dom
8912                              * eval:var:html
8913                              * 
8914                              */
8915                            window.eval(match[2]);
8916                         }
8917                     }
8918                 }
8919                 var el = document.getElementById(id);
8920                 if(el){el.parentNode.removeChild(el);}
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924             });
8925             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8926             return this;
8927         },
8928
8929         /**
8930          * Direct access to the UpdateManager update() method (takes the same parameters).
8931          * @param {String/Function} url The url for this request or a function to call to get the url
8932          * @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}
8933          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8934          * @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.
8935          * @return {Roo.Element} this
8936          */
8937         load : function(){
8938             var um = this.getUpdateManager();
8939             um.update.apply(um, arguments);
8940             return this;
8941         },
8942
8943         /**
8944         * Gets this element's UpdateManager
8945         * @return {Roo.UpdateManager} The UpdateManager
8946         */
8947         getUpdateManager : function(){
8948             if(!this.updateManager){
8949                 this.updateManager = new Roo.UpdateManager(this);
8950             }
8951             return this.updateManager;
8952         },
8953
8954         /**
8955          * Disables text selection for this element (normalized across browsers)
8956          * @return {Roo.Element} this
8957          */
8958         unselectable : function(){
8959             this.dom.unselectable = "on";
8960             this.swallowEvent("selectstart", true);
8961             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8962             this.addClass("x-unselectable");
8963             return this;
8964         },
8965
8966         /**
8967         * Calculates the x, y to center this element on the screen
8968         * @return {Array} The x, y values [x, y]
8969         */
8970         getCenterXY : function(){
8971             return this.getAlignToXY(document, 'c-c');
8972         },
8973
8974         /**
8975         * Centers the Element in either the viewport, or another Element.
8976         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8977         */
8978         center : function(centerIn){
8979             this.alignTo(centerIn || document, 'c-c');
8980             return this;
8981         },
8982
8983         /**
8984          * Tests various css rules/browsers to determine if this element uses a border box
8985          * @return {Boolean}
8986          */
8987         isBorderBox : function(){
8988             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8989         },
8990
8991         /**
8992          * Return a box {x, y, width, height} that can be used to set another elements
8993          * size/location to match this element.
8994          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8995          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8996          * @return {Object} box An object in the format {x, y, width, height}
8997          */
8998         getBox : function(contentBox, local){
8999             var xy;
9000             if(!local){
9001                 xy = this.getXY();
9002             }else{
9003                 var left = parseInt(this.getStyle("left"), 10) || 0;
9004                 var top = parseInt(this.getStyle("top"), 10) || 0;
9005                 xy = [left, top];
9006             }
9007             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9008             if(!contentBox){
9009                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9010             }else{
9011                 var l = this.getBorderWidth("l")+this.getPadding("l");
9012                 var r = this.getBorderWidth("r")+this.getPadding("r");
9013                 var t = this.getBorderWidth("t")+this.getPadding("t");
9014                 var b = this.getBorderWidth("b")+this.getPadding("b");
9015                 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)};
9016             }
9017             bx.right = bx.x + bx.width;
9018             bx.bottom = bx.y + bx.height;
9019             return bx;
9020         },
9021
9022         /**
9023          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9024          for more information about the sides.
9025          * @param {String} sides
9026          * @return {Number}
9027          */
9028         getFrameWidth : function(sides, onlyContentBox){
9029             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9030         },
9031
9032         /**
9033          * 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.
9034          * @param {Object} box The box to fill {x, y, width, height}
9035          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9036          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9037          * @return {Roo.Element} this
9038          */
9039         setBox : function(box, adjust, animate){
9040             var w = box.width, h = box.height;
9041             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9042                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9043                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9044             }
9045             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9046             return this;
9047         },
9048
9049         /**
9050          * Forces the browser to repaint this element
9051          * @return {Roo.Element} this
9052          */
9053          repaint : function(){
9054             var dom = this.dom;
9055             this.addClass("x-repaint");
9056             setTimeout(function(){
9057                 Roo.get(dom).removeClass("x-repaint");
9058             }, 1);
9059             return this;
9060         },
9061
9062         /**
9063          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9064          * then it returns the calculated width of the sides (see getPadding)
9065          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9066          * @return {Object/Number}
9067          */
9068         getMargins : function(side){
9069             if(!side){
9070                 return {
9071                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9072                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9073                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9074                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9075                 };
9076             }else{
9077                 return this.addStyles(side, El.margins);
9078              }
9079         },
9080
9081         // private
9082         addStyles : function(sides, styles){
9083             var val = 0, v, w;
9084             for(var i = 0, len = sides.length; i < len; i++){
9085                 v = this.getStyle(styles[sides.charAt(i)]);
9086                 if(v){
9087                      w = parseInt(v, 10);
9088                      if(w){ val += w; }
9089                 }
9090             }
9091             return val;
9092         },
9093
9094         /**
9095          * Creates a proxy element of this element
9096          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9097          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9098          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9099          * @return {Roo.Element} The new proxy element
9100          */
9101         createProxy : function(config, renderTo, matchBox){
9102             if(renderTo){
9103                 renderTo = Roo.getDom(renderTo);
9104             }else{
9105                 renderTo = document.body;
9106             }
9107             config = typeof config == "object" ?
9108                 config : {tag : "div", cls: config};
9109             var proxy = Roo.DomHelper.append(renderTo, config, true);
9110             if(matchBox){
9111                proxy.setBox(this.getBox());
9112             }
9113             return proxy;
9114         },
9115
9116         /**
9117          * Puts a mask over this element to disable user interaction. Requires core.css.
9118          * This method can only be applied to elements which accept child nodes.
9119          * @param {String} msg (optional) A message to display in the mask
9120          * @param {String} msgCls (optional) A css class to apply to the msg element
9121          * @return {Element} The mask  element
9122          */
9123         mask : function(msg, msgCls)
9124         {
9125             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9126                 this.setStyle("position", "relative");
9127             }
9128             if(!this._mask){
9129                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9130             }
9131             
9132             this.addClass("x-masked");
9133             this._mask.setDisplayed(true);
9134             
9135             // we wander
9136             var z = 0;
9137             var dom = this.dom;
9138             while (dom && dom.style) {
9139                 if (!isNaN(parseInt(dom.style.zIndex))) {
9140                     z = Math.max(z, parseInt(dom.style.zIndex));
9141                 }
9142                 dom = dom.parentNode;
9143             }
9144             // if we are masking the body - then it hides everything..
9145             if (this.dom == document.body) {
9146                 z = 1000000;
9147                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9148                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9149             }
9150            
9151             if(typeof msg == 'string'){
9152                 if(!this._maskMsg){
9153                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9154                         cls: "roo-el-mask-msg", 
9155                         cn: [
9156                             {
9157                                 tag: 'i',
9158                                 cls: 'fa fa-spinner fa-spin'
9159                             },
9160                             {
9161                                 tag: 'div'
9162                             }   
9163                         ]
9164                     }, true);
9165                 }
9166                 var mm = this._maskMsg;
9167                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9168                 if (mm.dom.lastChild) { // weird IE issue?
9169                     mm.dom.lastChild.innerHTML = msg;
9170                 }
9171                 mm.setDisplayed(true);
9172                 mm.center(this);
9173                 mm.setStyle('z-index', z + 102);
9174             }
9175             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9176                 this._mask.setHeight(this.getHeight());
9177             }
9178             this._mask.setStyle('z-index', z + 100);
9179             
9180             return this._mask;
9181         },
9182
9183         /**
9184          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9185          * it is cached for reuse.
9186          */
9187         unmask : function(removeEl){
9188             if(this._mask){
9189                 if(removeEl === true){
9190                     this._mask.remove();
9191                     delete this._mask;
9192                     if(this._maskMsg){
9193                         this._maskMsg.remove();
9194                         delete this._maskMsg;
9195                     }
9196                 }else{
9197                     this._mask.setDisplayed(false);
9198                     if(this._maskMsg){
9199                         this._maskMsg.setDisplayed(false);
9200                     }
9201                 }
9202             }
9203             this.removeClass("x-masked");
9204         },
9205
9206         /**
9207          * Returns true if this element is masked
9208          * @return {Boolean}
9209          */
9210         isMasked : function(){
9211             return this._mask && this._mask.isVisible();
9212         },
9213
9214         /**
9215          * Creates an iframe shim for this element to keep selects and other windowed objects from
9216          * showing through.
9217          * @return {Roo.Element} The new shim element
9218          */
9219         createShim : function(){
9220             var el = document.createElement('iframe');
9221             el.frameBorder = 'no';
9222             el.className = 'roo-shim';
9223             if(Roo.isIE && Roo.isSecure){
9224                 el.src = Roo.SSL_SECURE_URL;
9225             }
9226             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9227             shim.autoBoxAdjust = false;
9228             return shim;
9229         },
9230
9231         /**
9232          * Removes this element from the DOM and deletes it from the cache
9233          */
9234         remove : function(){
9235             if(this.dom.parentNode){
9236                 this.dom.parentNode.removeChild(this.dom);
9237             }
9238             delete El.cache[this.dom.id];
9239         },
9240
9241         /**
9242          * Sets up event handlers to add and remove a css class when the mouse is over this element
9243          * @param {String} className
9244          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9245          * mouseout events for children elements
9246          * @return {Roo.Element} this
9247          */
9248         addClassOnOver : function(className, preventFlicker){
9249             this.on("mouseover", function(){
9250                 Roo.fly(this, '_internal').addClass(className);
9251             }, this.dom);
9252             var removeFn = function(e){
9253                 if(preventFlicker !== true || !e.within(this, true)){
9254                     Roo.fly(this, '_internal').removeClass(className);
9255                 }
9256             };
9257             this.on("mouseout", removeFn, this.dom);
9258             return this;
9259         },
9260
9261         /**
9262          * Sets up event handlers to add and remove a css class when this element has the focus
9263          * @param {String} className
9264          * @return {Roo.Element} this
9265          */
9266         addClassOnFocus : function(className){
9267             this.on("focus", function(){
9268                 Roo.fly(this, '_internal').addClass(className);
9269             }, this.dom);
9270             this.on("blur", function(){
9271                 Roo.fly(this, '_internal').removeClass(className);
9272             }, this.dom);
9273             return this;
9274         },
9275         /**
9276          * 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)
9277          * @param {String} className
9278          * @return {Roo.Element} this
9279          */
9280         addClassOnClick : function(className){
9281             var dom = this.dom;
9282             this.on("mousedown", function(){
9283                 Roo.fly(dom, '_internal').addClass(className);
9284                 var d = Roo.get(document);
9285                 var fn = function(){
9286                     Roo.fly(dom, '_internal').removeClass(className);
9287                     d.removeListener("mouseup", fn);
9288                 };
9289                 d.on("mouseup", fn);
9290             });
9291             return this;
9292         },
9293
9294         /**
9295          * Stops the specified event from bubbling and optionally prevents the default action
9296          * @param {String} eventName
9297          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9298          * @return {Roo.Element} this
9299          */
9300         swallowEvent : function(eventName, preventDefault){
9301             var fn = function(e){
9302                 e.stopPropagation();
9303                 if(preventDefault){
9304                     e.preventDefault();
9305                 }
9306             };
9307             if(eventName instanceof Array){
9308                 for(var i = 0, len = eventName.length; i < len; i++){
9309                      this.on(eventName[i], fn);
9310                 }
9311                 return this;
9312             }
9313             this.on(eventName, fn);
9314             return this;
9315         },
9316
9317         /**
9318          * @private
9319          */
9320       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9321
9322         /**
9323          * Sizes this element to its parent element's dimensions performing
9324          * neccessary box adjustments.
9325          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9326          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9327          * @return {Roo.Element} this
9328          */
9329         fitToParent : function(monitorResize, targetParent) {
9330           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9331           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9332           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9333             return;
9334           }
9335           var p = Roo.get(targetParent || this.dom.parentNode);
9336           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9337           if (monitorResize === true) {
9338             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9339             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9340           }
9341           return this;
9342         },
9343
9344         /**
9345          * Gets the next sibling, skipping text nodes
9346          * @return {HTMLElement} The next sibling or null
9347          */
9348         getNextSibling : function(){
9349             var n = this.dom.nextSibling;
9350             while(n && n.nodeType != 1){
9351                 n = n.nextSibling;
9352             }
9353             return n;
9354         },
9355
9356         /**
9357          * Gets the previous sibling, skipping text nodes
9358          * @return {HTMLElement} The previous sibling or null
9359          */
9360         getPrevSibling : function(){
9361             var n = this.dom.previousSibling;
9362             while(n && n.nodeType != 1){
9363                 n = n.previousSibling;
9364             }
9365             return n;
9366         },
9367
9368
9369         /**
9370          * Appends the passed element(s) to this element
9371          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9372          * @return {Roo.Element} this
9373          */
9374         appendChild: function(el){
9375             el = Roo.get(el);
9376             el.appendTo(this);
9377             return this;
9378         },
9379
9380         /**
9381          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9382          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9383          * automatically generated with the specified attributes.
9384          * @param {HTMLElement} insertBefore (optional) a child element of this element
9385          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9386          * @return {Roo.Element} The new child element
9387          */
9388         createChild: function(config, insertBefore, returnDom){
9389             config = config || {tag:'div'};
9390             if(insertBefore){
9391                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9392             }
9393             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9394         },
9395
9396         /**
9397          * Appends this element to the passed element
9398          * @param {String/HTMLElement/Element} el The new parent element
9399          * @return {Roo.Element} this
9400          */
9401         appendTo: function(el){
9402             el = Roo.getDom(el);
9403             el.appendChild(this.dom);
9404             return this;
9405         },
9406
9407         /**
9408          * Inserts this element before the passed element in the DOM
9409          * @param {String/HTMLElement/Element} el The element to insert before
9410          * @return {Roo.Element} this
9411          */
9412         insertBefore: function(el){
9413             el = Roo.getDom(el);
9414             el.parentNode.insertBefore(this.dom, el);
9415             return this;
9416         },
9417
9418         /**
9419          * Inserts this element after the passed element in the DOM
9420          * @param {String/HTMLElement/Element} el The element to insert after
9421          * @return {Roo.Element} this
9422          */
9423         insertAfter: function(el){
9424             el = Roo.getDom(el);
9425             el.parentNode.insertBefore(this.dom, el.nextSibling);
9426             return this;
9427         },
9428
9429         /**
9430          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9431          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9432          * @return {Roo.Element} The new child
9433          */
9434         insertFirst: function(el, returnDom){
9435             el = el || {};
9436             if(typeof el == 'object' && !el.nodeType){ // dh config
9437                 return this.createChild(el, this.dom.firstChild, returnDom);
9438             }else{
9439                 el = Roo.getDom(el);
9440                 this.dom.insertBefore(el, this.dom.firstChild);
9441                 return !returnDom ? Roo.get(el) : el;
9442             }
9443         },
9444
9445         /**
9446          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9447          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9448          * @param {String} where (optional) 'before' or 'after' defaults to before
9449          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9450          * @return {Roo.Element} the inserted Element
9451          */
9452         insertSibling: function(el, where, returnDom){
9453             where = where ? where.toLowerCase() : 'before';
9454             el = el || {};
9455             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9456
9457             if(typeof el == 'object' && !el.nodeType){ // dh config
9458                 if(where == 'after' && !this.dom.nextSibling){
9459                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9460                 }else{
9461                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9462                 }
9463
9464             }else{
9465                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9466                             where == 'before' ? this.dom : this.dom.nextSibling);
9467                 if(!returnDom){
9468                     rt = Roo.get(rt);
9469                 }
9470             }
9471             return rt;
9472         },
9473
9474         /**
9475          * Creates and wraps this element with another element
9476          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9477          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9478          * @return {HTMLElement/Element} The newly created wrapper element
9479          */
9480         wrap: function(config, returnDom){
9481             if(!config){
9482                 config = {tag: "div"};
9483             }
9484             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9485             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9486             return newEl;
9487         },
9488
9489         /**
9490          * Replaces the passed element with this element
9491          * @param {String/HTMLElement/Element} el The element to replace
9492          * @return {Roo.Element} this
9493          */
9494         replace: function(el){
9495             el = Roo.get(el);
9496             this.insertBefore(el);
9497             el.remove();
9498             return this;
9499         },
9500
9501         /**
9502          * Inserts an html fragment into this element
9503          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9504          * @param {String} html The HTML fragment
9505          * @param {Boolean} returnEl True to return an Roo.Element
9506          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9507          */
9508         insertHtml : function(where, html, returnEl){
9509             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9510             return returnEl ? Roo.get(el) : el;
9511         },
9512
9513         /**
9514          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9515          * @param {Object} o The object with the attributes
9516          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9517          * @return {Roo.Element} this
9518          */
9519         set : function(o, useSet){
9520             var el = this.dom;
9521             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9522             for(var attr in o){
9523                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9524                 if(attr=="cls"){
9525                     el.className = o["cls"];
9526                 }else{
9527                     if(useSet) {
9528                         el.setAttribute(attr, o[attr]);
9529                     } else {
9530                         el[attr] = o[attr];
9531                     }
9532                 }
9533             }
9534             if(o.style){
9535                 Roo.DomHelper.applyStyles(el, o.style);
9536             }
9537             return this;
9538         },
9539
9540         /**
9541          * Convenience method for constructing a KeyMap
9542          * @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:
9543          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9544          * @param {Function} fn The function to call
9545          * @param {Object} scope (optional) The scope of the function
9546          * @return {Roo.KeyMap} The KeyMap created
9547          */
9548         addKeyListener : function(key, fn, scope){
9549             var config;
9550             if(typeof key != "object" || key instanceof Array){
9551                 config = {
9552                     key: key,
9553                     fn: fn,
9554                     scope: scope
9555                 };
9556             }else{
9557                 config = {
9558                     key : key.key,
9559                     shift : key.shift,
9560                     ctrl : key.ctrl,
9561                     alt : key.alt,
9562                     fn: fn,
9563                     scope: scope
9564                 };
9565             }
9566             return new Roo.KeyMap(this, config);
9567         },
9568
9569         /**
9570          * Creates a KeyMap for this element
9571          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9572          * @return {Roo.KeyMap} The KeyMap created
9573          */
9574         addKeyMap : function(config){
9575             return new Roo.KeyMap(this, config);
9576         },
9577
9578         /**
9579          * Returns true if this element is scrollable.
9580          * @return {Boolean}
9581          */
9582          isScrollable : function(){
9583             var dom = this.dom;
9584             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9585         },
9586
9587         /**
9588          * 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().
9589          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9590          * @param {Number} value The new scroll value
9591          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9592          * @return {Element} this
9593          */
9594
9595         scrollTo : function(side, value, animate){
9596             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9597             if(!animate || !A){
9598                 this.dom[prop] = value;
9599             }else{
9600                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9601                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9602             }
9603             return this;
9604         },
9605
9606         /**
9607          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9608          * within this element's scrollable range.
9609          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9610          * @param {Number} distance How far to scroll the element in pixels
9611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9612          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9613          * was scrolled as far as it could go.
9614          */
9615          scroll : function(direction, distance, animate){
9616              if(!this.isScrollable()){
9617                  return;
9618              }
9619              var el = this.dom;
9620              var l = el.scrollLeft, t = el.scrollTop;
9621              var w = el.scrollWidth, h = el.scrollHeight;
9622              var cw = el.clientWidth, ch = el.clientHeight;
9623              direction = direction.toLowerCase();
9624              var scrolled = false;
9625              var a = this.preanim(arguments, 2);
9626              switch(direction){
9627                  case "l":
9628                  case "left":
9629                      if(w - l > cw){
9630                          var v = Math.min(l + distance, w-cw);
9631                          this.scrollTo("left", v, a);
9632                          scrolled = true;
9633                      }
9634                      break;
9635                 case "r":
9636                 case "right":
9637                      if(l > 0){
9638                          var v = Math.max(l - distance, 0);
9639                          this.scrollTo("left", v, a);
9640                          scrolled = true;
9641                      }
9642                      break;
9643                 case "t":
9644                 case "top":
9645                 case "up":
9646                      if(t > 0){
9647                          var v = Math.max(t - distance, 0);
9648                          this.scrollTo("top", v, a);
9649                          scrolled = true;
9650                      }
9651                      break;
9652                 case "b":
9653                 case "bottom":
9654                 case "down":
9655                      if(h - t > ch){
9656                          var v = Math.min(t + distance, h-ch);
9657                          this.scrollTo("top", v, a);
9658                          scrolled = true;
9659                      }
9660                      break;
9661              }
9662              return scrolled;
9663         },
9664
9665         /**
9666          * Translates the passed page coordinates into left/top css values for this element
9667          * @param {Number/Array} x The page x or an array containing [x, y]
9668          * @param {Number} y The page y
9669          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9670          */
9671         translatePoints : function(x, y){
9672             if(typeof x == 'object' || x instanceof Array){
9673                 y = x[1]; x = x[0];
9674             }
9675             var p = this.getStyle('position');
9676             var o = this.getXY();
9677
9678             var l = parseInt(this.getStyle('left'), 10);
9679             var t = parseInt(this.getStyle('top'), 10);
9680
9681             if(isNaN(l)){
9682                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9683             }
9684             if(isNaN(t)){
9685                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9686             }
9687
9688             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9689         },
9690
9691         /**
9692          * Returns the current scroll position of the element.
9693          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9694          */
9695         getScroll : function(){
9696             var d = this.dom, doc = document;
9697             if(d == doc || d == doc.body){
9698                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9699                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9700                 return {left: l, top: t};
9701             }else{
9702                 return {left: d.scrollLeft, top: d.scrollTop};
9703             }
9704         },
9705
9706         /**
9707          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9708          * are convert to standard 6 digit hex color.
9709          * @param {String} attr The css attribute
9710          * @param {String} defaultValue The default value to use when a valid color isn't found
9711          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9712          * YUI color anims.
9713          */
9714         getColor : function(attr, defaultValue, prefix){
9715             var v = this.getStyle(attr);
9716             if(!v || v == "transparent" || v == "inherit") {
9717                 return defaultValue;
9718             }
9719             var color = typeof prefix == "undefined" ? "#" : prefix;
9720             if(v.substr(0, 4) == "rgb("){
9721                 var rvs = v.slice(4, v.length -1).split(",");
9722                 for(var i = 0; i < 3; i++){
9723                     var h = parseInt(rvs[i]).toString(16);
9724                     if(h < 16){
9725                         h = "0" + h;
9726                     }
9727                     color += h;
9728                 }
9729             } else {
9730                 if(v.substr(0, 1) == "#"){
9731                     if(v.length == 4) {
9732                         for(var i = 1; i < 4; i++){
9733                             var c = v.charAt(i);
9734                             color +=  c + c;
9735                         }
9736                     }else if(v.length == 7){
9737                         color += v.substr(1);
9738                     }
9739                 }
9740             }
9741             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9742         },
9743
9744         /**
9745          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9746          * gradient background, rounded corners and a 4-way shadow.
9747          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9748          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9749          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9750          * @return {Roo.Element} this
9751          */
9752         boxWrap : function(cls){
9753             cls = cls || 'x-box';
9754             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9755             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9756             return el;
9757         },
9758
9759         /**
9760          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9761          * @param {String} namespace The namespace in which to look for the attribute
9762          * @param {String} name The attribute name
9763          * @return {String} The attribute value
9764          */
9765         getAttributeNS : Roo.isIE ? function(ns, name){
9766             var d = this.dom;
9767             var type = typeof d[ns+":"+name];
9768             if(type != 'undefined' && type != 'unknown'){
9769                 return d[ns+":"+name];
9770             }
9771             return d[name];
9772         } : function(ns, name){
9773             var d = this.dom;
9774             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9775         },
9776         
9777         
9778         /**
9779          * Sets or Returns the value the dom attribute value
9780          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9781          * @param {String} value (optional) The value to set the attribute to
9782          * @return {String} The attribute value
9783          */
9784         attr : function(name){
9785             if (arguments.length > 1) {
9786                 this.dom.setAttribute(name, arguments[1]);
9787                 return arguments[1];
9788             }
9789             if (typeof(name) == 'object') {
9790                 for(var i in name) {
9791                     this.attr(i, name[i]);
9792                 }
9793                 return name;
9794             }
9795             
9796             
9797             if (!this.dom.hasAttribute(name)) {
9798                 return undefined;
9799             }
9800             return this.dom.getAttribute(name);
9801         }
9802         
9803         
9804         
9805     };
9806
9807     var ep = El.prototype;
9808
9809     /**
9810      * Appends an event handler (Shorthand for addListener)
9811      * @param {String}   eventName     The type of event to append
9812      * @param {Function} fn        The method the event invokes
9813      * @param {Object} scope       (optional) The scope (this object) of the fn
9814      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9815      * @method
9816      */
9817     ep.on = ep.addListener;
9818         // backwards compat
9819     ep.mon = ep.addListener;
9820
9821     /**
9822      * Removes an event handler from this element (shorthand for removeListener)
9823      * @param {String} eventName the type of event to remove
9824      * @param {Function} fn the method the event invokes
9825      * @return {Roo.Element} this
9826      * @method
9827      */
9828     ep.un = ep.removeListener;
9829
9830     /**
9831      * true to automatically adjust width and height settings for box-model issues (default to true)
9832      */
9833     ep.autoBoxAdjust = true;
9834
9835     // private
9836     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9837
9838     // private
9839     El.addUnits = function(v, defaultUnit){
9840         if(v === "" || v == "auto"){
9841             return v;
9842         }
9843         if(v === undefined){
9844             return '';
9845         }
9846         if(typeof v == "number" || !El.unitPattern.test(v)){
9847             return v + (defaultUnit || 'px');
9848         }
9849         return v;
9850     };
9851
9852     // special markup used throughout Roo when box wrapping elements
9853     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>';
9854     /**
9855      * Visibility mode constant - Use visibility to hide element
9856      * @static
9857      * @type Number
9858      */
9859     El.VISIBILITY = 1;
9860     /**
9861      * Visibility mode constant - Use display to hide element
9862      * @static
9863      * @type Number
9864      */
9865     El.DISPLAY = 2;
9866
9867     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9868     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9869     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9870
9871
9872
9873     /**
9874      * @private
9875      */
9876     El.cache = {};
9877
9878     var docEl;
9879
9880     /**
9881      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9882      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9883      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9884      * @return {Element} The Element object
9885      * @static
9886      */
9887     El.get = function(el){
9888         var ex, elm, id;
9889         if(!el){ return null; }
9890         if(typeof el == "string"){ // element id
9891             if(!(elm = document.getElementById(el))){
9892                 return null;
9893             }
9894             if(ex = El.cache[el]){
9895                 ex.dom = elm;
9896             }else{
9897                 ex = El.cache[el] = new El(elm);
9898             }
9899             return ex;
9900         }else if(el.tagName){ // dom element
9901             if(!(id = el.id)){
9902                 id = Roo.id(el);
9903             }
9904             if(ex = El.cache[id]){
9905                 ex.dom = el;
9906             }else{
9907                 ex = El.cache[id] = new El(el);
9908             }
9909             return ex;
9910         }else if(el instanceof El){
9911             if(el != docEl){
9912                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9913                                                               // catch case where it hasn't been appended
9914                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9915             }
9916             return el;
9917         }else if(el.isComposite){
9918             return el;
9919         }else if(el instanceof Array){
9920             return El.select(el);
9921         }else if(el == document){
9922             // create a bogus element object representing the document object
9923             if(!docEl){
9924                 var f = function(){};
9925                 f.prototype = El.prototype;
9926                 docEl = new f();
9927                 docEl.dom = document;
9928             }
9929             return docEl;
9930         }
9931         return null;
9932     };
9933
9934     // private
9935     El.uncache = function(el){
9936         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9937             if(a[i]){
9938                 delete El.cache[a[i].id || a[i]];
9939             }
9940         }
9941     };
9942
9943     // private
9944     // Garbage collection - uncache elements/purge listeners on orphaned elements
9945     // so we don't hold a reference and cause the browser to retain them
9946     El.garbageCollect = function(){
9947         if(!Roo.enableGarbageCollector){
9948             clearInterval(El.collectorThread);
9949             return;
9950         }
9951         for(var eid in El.cache){
9952             var el = El.cache[eid], d = el.dom;
9953             // -------------------------------------------------------
9954             // Determining what is garbage:
9955             // -------------------------------------------------------
9956             // !d
9957             // dom node is null, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.parentNode
9960             // no parentNode == direct orphan, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.offsetParent && !document.getElementById(eid)
9963             // display none elements have no offsetParent so we will
9964             // also try to look it up by it's id. However, check
9965             // offsetParent first so we don't do unneeded lookups.
9966             // This enables collection of elements that are not orphans
9967             // directly, but somewhere up the line they have an orphan
9968             // parent.
9969             // -------------------------------------------------------
9970             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9971                 delete El.cache[eid];
9972                 if(d && Roo.enableListenerCollection){
9973                     E.purgeElement(d);
9974                 }
9975             }
9976         }
9977     }
9978     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9979
9980
9981     // dom is optional
9982     El.Flyweight = function(dom){
9983         this.dom = dom;
9984     };
9985     El.Flyweight.prototype = El.prototype;
9986
9987     El._flyweights = {};
9988     /**
9989      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9990      * the dom node can be overwritten by other code.
9991      * @param {String/HTMLElement} el The dom node or id
9992      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9993      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9994      * @static
9995      * @return {Element} The shared Element object
9996      */
9997     El.fly = function(el, named){
9998         named = named || '_global';
9999         el = Roo.getDom(el);
10000         if(!el){
10001             return null;
10002         }
10003         if(!El._flyweights[named]){
10004             El._flyweights[named] = new El.Flyweight();
10005         }
10006         El._flyweights[named].dom = el;
10007         return El._flyweights[named];
10008     };
10009
10010     /**
10011      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10012      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10013      * Shorthand of {@link Roo.Element#get}
10014      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10015      * @return {Element} The Element object
10016      * @member Roo
10017      * @method get
10018      */
10019     Roo.get = El.get;
10020     /**
10021      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10022      * the dom node can be overwritten by other code.
10023      * Shorthand of {@link Roo.Element#fly}
10024      * @param {String/HTMLElement} el The dom node or id
10025      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10026      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10027      * @static
10028      * @return {Element} The shared Element object
10029      * @member Roo
10030      * @method fly
10031      */
10032     Roo.fly = El.fly;
10033
10034     // speedy lookup for elements never to box adjust
10035     var noBoxAdjust = Roo.isStrict ? {
10036         select:1
10037     } : {
10038         input:1, select:1, textarea:1
10039     };
10040     if(Roo.isIE || Roo.isGecko){
10041         noBoxAdjust['button'] = 1;
10042     }
10043
10044
10045     Roo.EventManager.on(window, 'unload', function(){
10046         delete El.cache;
10047         delete El._flyweights;
10048     });
10049 })();
10050
10051
10052
10053
10054 if(Roo.DomQuery){
10055     Roo.Element.selectorFunction = Roo.DomQuery.select;
10056 }
10057
10058 Roo.Element.select = function(selector, unique, root){
10059     var els;
10060     if(typeof selector == "string"){
10061         els = Roo.Element.selectorFunction(selector, root);
10062     }else if(selector.length !== undefined){
10063         els = selector;
10064     }else{
10065         throw "Invalid selector";
10066     }
10067     if(unique === true){
10068         return new Roo.CompositeElement(els);
10069     }else{
10070         return new Roo.CompositeElementLite(els);
10071     }
10072 };
10073 /**
10074  * Selects elements based on the passed CSS selector to enable working on them as 1.
10075  * @param {String/Array} selector The CSS selector or an array of elements
10076  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10077  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10078  * @return {CompositeElementLite/CompositeElement}
10079  * @member Roo
10080  * @method select
10081  */
10082 Roo.select = Roo.Element.select;
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097 /*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108
10109
10110 //Notifies Element that fx methods are available
10111 Roo.enableFx = true;
10112
10113 /**
10114  * @class Roo.Fx
10115  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10116  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10117  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10118  * Element effects to work.</p><br/>
10119  *
10120  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10121  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10122  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10123  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10124  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10125  * expected results and should be done with care.</p><br/>
10126  *
10127  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10128  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10129 <pre>
10130 Value  Description
10131 -----  -----------------------------
10132 tl     The top left corner
10133 t      The center of the top edge
10134 tr     The top right corner
10135 l      The center of the left edge
10136 r      The center of the right edge
10137 bl     The bottom left corner
10138 b      The center of the bottom edge
10139 br     The bottom right corner
10140 </pre>
10141  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10142  * below are common options that can be passed to any Fx method.</b>
10143  * @cfg {Function} callback A function called when the effect is finished
10144  * @cfg {Object} scope The scope of the effect function
10145  * @cfg {String} easing A valid Easing value for the effect
10146  * @cfg {String} afterCls A css class to apply after the effect
10147  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10148  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10149  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10150  * effects that end with the element being visually hidden, ignored otherwise)
10151  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10152  * a function which returns such a specification that will be applied to the Element after the effect finishes
10153  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10154  * @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
10155  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10156  */
10157 Roo.Fx = {
10158         /**
10159          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10160          * origin for the slide effect.  This function automatically handles wrapping the element with
10161          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10162          * Usage:
10163          *<pre><code>
10164 // default: slide the element in from the top
10165 el.slideIn();
10166
10167 // custom: slide the element in from the right with a 2-second duration
10168 el.slideIn('r', { duration: 2 });
10169
10170 // common config options shown with default values
10171 el.slideIn('t', {
10172     easing: 'easeOut',
10173     duration: .5
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideIn : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // fix display to visibility
10189             this.fixDisplay();
10190
10191             // restore values after effect
10192             var r = this.getFxRestore();
10193             var b = this.getBox();
10194             // fixed size for slide
10195             this.setSize(b);
10196
10197             // wrap if needed
10198             var wrap = this.fxWrap(r.pos, o, "hidden");
10199
10200             var st = this.dom.style;
10201             st.visibility = "visible";
10202             st.position = "absolute";
10203
10204             // clear out temp styles after slide and unwrap
10205             var after = function(){
10206                 el.fxUnwrap(wrap, r.pos, o);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 el.afterFx(o);
10210             };
10211             // time to calc the positions
10212             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10213
10214             switch(anchor.toLowerCase()){
10215                 case "t":
10216                     wrap.setSize(b.width, 0);
10217                     st.left = st.bottom = "0";
10218                     a = {height: bh};
10219                 break;
10220                 case "l":
10221                     wrap.setSize(0, b.height);
10222                     st.right = st.top = "0";
10223                     a = {width: bw};
10224                 break;
10225                 case "r":
10226                     wrap.setSize(0, b.height);
10227                     wrap.setX(b.right);
10228                     st.left = st.top = "0";
10229                     a = {width: bw, points: pt};
10230                 break;
10231                 case "b":
10232                     wrap.setSize(b.width, 0);
10233                     wrap.setY(b.bottom);
10234                     st.left = st.top = "0";
10235                     a = {height: bh, points: pt};
10236                 break;
10237                 case "tl":
10238                     wrap.setSize(0, 0);
10239                     st.right = st.bottom = "0";
10240                     a = {width: bw, height: bh};
10241                 break;
10242                 case "bl":
10243                     wrap.setSize(0, 0);
10244                     wrap.setY(b.y+b.height);
10245                     st.right = st.top = "0";
10246                     a = {width: bw, height: bh, points: pt};
10247                 break;
10248                 case "br":
10249                     wrap.setSize(0, 0);
10250                     wrap.setXY([b.right, b.bottom]);
10251                     st.left = st.top = "0";
10252                     a = {width: bw, height: bh, points: pt};
10253                 break;
10254                 case "tr":
10255                     wrap.setSize(0, 0);
10256                     wrap.setX(b.x+b.width);
10257                     st.left = st.bottom = "0";
10258                     a = {width: bw, height: bh, points: pt};
10259                 break;
10260             }
10261             this.dom.style.visibility = "visible";
10262             wrap.show();
10263
10264             arguments.callee.anim = wrap.fxanim(a,
10265                 o,
10266                 'motion',
10267                 .5,
10268                 'easeOut', after);
10269         });
10270         return this;
10271     },
10272     
10273         /**
10274          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10275          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10276          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10277          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10278          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10279          * Usage:
10280          *<pre><code>
10281 // default: slide the element out to the top
10282 el.slideOut();
10283
10284 // custom: slide the element out to the right with a 2-second duration
10285 el.slideOut('r', { duration: 2 });
10286
10287 // common config options shown with default values
10288 el.slideOut('t', {
10289     easing: 'easeOut',
10290     duration: .5,
10291     remove: false,
10292     useDisplay: false
10293 });
10294 </code></pre>
10295          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10296          * @param {Object} options (optional) Object literal with any of the Fx config options
10297          * @return {Roo.Element} The Element
10298          */
10299     slideOut : function(anchor, o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302
10303         el.queueFx(o, function(){
10304
10305             anchor = anchor || "t";
10306
10307             // restore values after effect
10308             var r = this.getFxRestore();
10309             
10310             var b = this.getBox();
10311             // fixed size for slide
10312             this.setSize(b);
10313
10314             // wrap if needed
10315             var wrap = this.fxWrap(r.pos, o, "visible");
10316
10317             var st = this.dom.style;
10318             st.visibility = "visible";
10319             st.position = "absolute";
10320
10321             wrap.setSize(b);
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.fxUnwrap(wrap, r.pos, o);
10331
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             var a, zero = {to: 0};
10339             switch(anchor.toLowerCase()){
10340                 case "t":
10341                     st.left = st.bottom = "0";
10342                     a = {height: zero};
10343                 break;
10344                 case "l":
10345                     st.right = st.top = "0";
10346                     a = {width: zero};
10347                 break;
10348                 case "r":
10349                     st.left = st.top = "0";
10350                     a = {width: zero, points: {to:[b.right, b.y]}};
10351                 break;
10352                 case "b":
10353                     st.left = st.top = "0";
10354                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10355                 break;
10356                 case "tl":
10357                     st.right = st.bottom = "0";
10358                     a = {width: zero, height: zero};
10359                 break;
10360                 case "bl":
10361                     st.right = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10363                 break;
10364                 case "br":
10365                     st.left = st.top = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10367                 break;
10368                 case "tr":
10369                     st.left = st.bottom = "0";
10370                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10371                 break;
10372             }
10373
10374             arguments.callee.anim = wrap.fxanim(a,
10375                 o,
10376                 'motion',
10377                 .5,
10378                 "easeOut", after);
10379         });
10380         return this;
10381     },
10382
10383         /**
10384          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10385          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10386          * The element must be removed from the DOM using the 'remove' config option if desired.
10387          * Usage:
10388          *<pre><code>
10389 // default
10390 el.puff();
10391
10392 // common config options shown with default values
10393 el.puff({
10394     easing: 'easeOut',
10395     duration: .5,
10396     remove: false,
10397     useDisplay: false
10398 });
10399 </code></pre>
10400          * @param {Object} options (optional) Object literal with any of the Fx config options
10401          * @return {Roo.Element} The Element
10402          */
10403     puff : function(o){
10404         var el = this.getFxEl();
10405         o = o || {};
10406
10407         el.queueFx(o, function(){
10408             this.clearOpacity();
10409             this.show();
10410
10411             // restore values after effect
10412             var r = this.getFxRestore();
10413             var st = this.dom.style;
10414
10415             var after = function(){
10416                 if(o.useDisplay){
10417                     el.setDisplayed(false);
10418                 }else{
10419                     el.hide();
10420                 }
10421
10422                 el.clearOpacity();
10423
10424                 el.setPositioning(r.pos);
10425                 st.width = r.width;
10426                 st.height = r.height;
10427                 st.fontSize = '';
10428                 el.afterFx(o);
10429             };
10430
10431             var width = this.getWidth();
10432             var height = this.getHeight();
10433
10434             arguments.callee.anim = this.fxanim({
10435                     width : {to: this.adjustWidth(width * 2)},
10436                     height : {to: this.adjustHeight(height * 2)},
10437                     points : {by: [-(width * .5), -(height * .5)]},
10438                     opacity : {to: 0},
10439                     fontSize: {to:200, unit: "%"}
10440                 },
10441                 o,
10442                 'motion',
10443                 .5,
10444                 "easeOut", after);
10445         });
10446         return this;
10447     },
10448
10449         /**
10450          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10451          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10452          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10453          * Usage:
10454          *<pre><code>
10455 // default
10456 el.switchOff();
10457
10458 // all config options shown with default values
10459 el.switchOff({
10460     easing: 'easeIn',
10461     duration: .3,
10462     remove: false,
10463     useDisplay: false
10464 });
10465 </code></pre>
10466          * @param {Object} options (optional) Object literal with any of the Fx config options
10467          * @return {Roo.Element} The Element
10468          */
10469     switchOff : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472
10473         el.queueFx(o, function(){
10474             this.clearOpacity();
10475             this.clip();
10476
10477             // restore values after effect
10478             var r = this.getFxRestore();
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10497                 this.clearOpacity();
10498                 (function(){
10499                     this.fxanim({
10500                         height:{to:1},
10501                         points:{by:[0, this.getHeight() * .5]}
10502                     }, o, 'motion', 0.3, 'easeIn', after);
10503                 }).defer(100, this);
10504             });
10505         });
10506         return this;
10507     },
10508
10509     /**
10510      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10511      * changed using the "attr" config option) and then fading back to the original color. If no original
10512      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10513      * Usage:
10514 <pre><code>
10515 // default: highlight background to yellow
10516 el.highlight();
10517
10518 // custom: highlight foreground text to blue for 2 seconds
10519 el.highlight("0000ff", { attr: 'color', duration: 2 });
10520
10521 // common config options shown with default values
10522 el.highlight("ffff9c", {
10523     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10524     endColor: (current color) or "ffffff",
10525     easing: 'easeIn',
10526     duration: 1
10527 });
10528 </code></pre>
10529      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10530      * @param {Object} options (optional) Object literal with any of the Fx config options
10531      * @return {Roo.Element} The Element
10532      */ 
10533     highlight : function(color, o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536
10537         el.queueFx(o, function(){
10538             color = color || "ffff9c";
10539             attr = o.attr || "backgroundColor";
10540
10541             this.clearOpacity();
10542             this.show();
10543
10544             var origColor = this.getColor(attr);
10545             var restoreColor = this.dom.style[attr];
10546             endColor = (o.endColor || origColor) || "ffffff";
10547
10548             var after = function(){
10549                 el.dom.style[attr] = restoreColor;
10550                 el.afterFx(o);
10551             };
10552
10553             var a = {};
10554             a[attr] = {from: color, to: endColor};
10555             arguments.callee.anim = this.fxanim(a,
10556                 o,
10557                 'color',
10558                 1,
10559                 'easeIn', after);
10560         });
10561         return this;
10562     },
10563
10564    /**
10565     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10566     * Usage:
10567 <pre><code>
10568 // default: a single light blue ripple
10569 el.frame();
10570
10571 // custom: 3 red ripples lasting 3 seconds total
10572 el.frame("ff0000", 3, { duration: 3 });
10573
10574 // common config options shown with default values
10575 el.frame("C3DAF9", 1, {
10576     duration: 1 //duration of entire animation (not each individual ripple)
10577     // Note: Easing is not configurable and will be ignored if included
10578 });
10579 </code></pre>
10580     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10581     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10582     * @param {Object} options (optional) Object literal with any of the Fx config options
10583     * @return {Roo.Element} The Element
10584     */
10585     frame : function(color, count, o){
10586         var el = this.getFxEl();
10587         o = o || {};
10588
10589         el.queueFx(o, function(){
10590             color = color || "#C3DAF9";
10591             if(color.length == 6){
10592                 color = "#" + color;
10593             }
10594             count = count || 1;
10595             duration = o.duration || 1;
10596             this.show();
10597
10598             var b = this.getBox();
10599             var animFn = function(){
10600                 var proxy = this.createProxy({
10601
10602                      style:{
10603                         visbility:"hidden",
10604                         position:"absolute",
10605                         "z-index":"35000", // yee haw
10606                         border:"0px solid " + color
10607                      }
10608                   });
10609                 var scale = Roo.isBorderBox ? 2 : 1;
10610                 proxy.animate({
10611                     top:{from:b.y, to:b.y - 20},
10612                     left:{from:b.x, to:b.x - 20},
10613                     borderWidth:{from:0, to:10},
10614                     opacity:{from:1, to:0},
10615                     height:{from:b.height, to:(b.height + (20*scale))},
10616                     width:{from:b.width, to:(b.width + (20*scale))}
10617                 }, duration, function(){
10618                     proxy.remove();
10619                 });
10620                 if(--count > 0){
10621                      animFn.defer((duration/2)*1000, this);
10622                 }else{
10623                     el.afterFx(o);
10624                 }
10625             };
10626             animFn.call(this);
10627         });
10628         return this;
10629     },
10630
10631    /**
10632     * Creates a pause before any subsequent queued effects begin.  If there are
10633     * no effects queued after the pause it will have no effect.
10634     * Usage:
10635 <pre><code>
10636 el.pause(1);
10637 </code></pre>
10638     * @param {Number} seconds The length of time to pause (in seconds)
10639     * @return {Roo.Element} The Element
10640     */
10641     pause : function(seconds){
10642         var el = this.getFxEl();
10643         var o = {};
10644
10645         el.queueFx(o, function(){
10646             setTimeout(function(){
10647                 el.afterFx(o);
10648             }, seconds * 1000);
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10655     * using the "endOpacity" config option.
10656     * Usage:
10657 <pre><code>
10658 // default: fade in from opacity 0 to 100%
10659 el.fadeIn();
10660
10661 // custom: fade in from opacity 0 to 75% over 2 seconds
10662 el.fadeIn({ endOpacity: .75, duration: 2});
10663
10664 // common config options shown with default values
10665 el.fadeIn({
10666     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10667     easing: 'easeOut',
10668     duration: .5
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeIn : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             this.setOpacity(0);
10679             this.fixDisplay();
10680             this.dom.style.visibility = 'visible';
10681             var to = o.endOpacity || 1;
10682             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10683                 o, null, .5, "easeOut", function(){
10684                 if(to == 1){
10685                     this.clearOpacity();
10686                 }
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10695     * using the "endOpacity" config option.
10696     * Usage:
10697 <pre><code>
10698 // default: fade out from the element's current opacity to 0
10699 el.fadeOut();
10700
10701 // custom: fade out from the element's current opacity to 25% over 2 seconds
10702 el.fadeOut({ endOpacity: .25, duration: 2});
10703
10704 // common config options shown with default values
10705 el.fadeOut({
10706     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10707     easing: 'easeOut',
10708     duration: .5
10709     remove: false,
10710     useDisplay: false
10711 });
10712 </code></pre>
10713     * @param {Object} options (optional) Object literal with any of the Fx config options
10714     * @return {Roo.Element} The Element
10715     */
10716     fadeOut : function(o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719         el.queueFx(o, function(){
10720             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10721                 o, null, .5, "easeOut", function(){
10722                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10723                      this.dom.style.display = "none";
10724                 }else{
10725                      this.dom.style.visibility = "hidden";
10726                 }
10727                 this.clearOpacity();
10728                 el.afterFx(o);
10729             });
10730         });
10731         return this;
10732     },
10733
10734    /**
10735     * Animates the transition of an element's dimensions from a starting height/width
10736     * to an ending height/width.
10737     * Usage:
10738 <pre><code>
10739 // change height and width to 100x100 pixels
10740 el.scale(100, 100);
10741
10742 // common config options shown with default values.  The height and width will default to
10743 // the element's existing values if passed as null.
10744 el.scale(
10745     [element's width],
10746     [element's height], {
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Number} width  The new width (pass undefined to keep the original width)
10752     * @param {Number} height  The new height (pass undefined to keep the original height)
10753     * @param {Object} options (optional) Object literal with any of the Fx config options
10754     * @return {Roo.Element} The Element
10755     */
10756     scale : function(w, h, o){
10757         this.shift(Roo.apply({}, o, {
10758             width: w,
10759             height: h
10760         }));
10761         return this;
10762     },
10763
10764    /**
10765     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10766     * Any of these properties not specified in the config object will not be changed.  This effect 
10767     * requires that at least one new dimension, position or opacity setting must be passed in on
10768     * the config object in order for the function to have any effect.
10769     * Usage:
10770 <pre><code>
10771 // slide the element horizontally to x position 200 while changing the height and opacity
10772 el.shift({ x: 200, height: 50, opacity: .8 });
10773
10774 // common config options shown with default values.
10775 el.shift({
10776     width: [element's width],
10777     height: [element's height],
10778     x: [element's x position],
10779     y: [element's y position],
10780     opacity: [element's opacity],
10781     easing: 'easeOut',
10782     duration: .35
10783 });
10784 </code></pre>
10785     * @param {Object} options  Object literal with any of the Fx config options
10786     * @return {Roo.Element} The Element
10787     */
10788     shift : function(o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791         el.queueFx(o, function(){
10792             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10793             if(w !== undefined){
10794                 a.width = {to: this.adjustWidth(w)};
10795             }
10796             if(h !== undefined){
10797                 a.height = {to: this.adjustHeight(h)};
10798             }
10799             if(x !== undefined || y !== undefined){
10800                 a.points = {to: [
10801                     x !== undefined ? x : this.getX(),
10802                     y !== undefined ? y : this.getY()
10803                 ]};
10804             }
10805             if(op !== undefined){
10806                 a.opacity = {to: op};
10807             }
10808             if(o.xy !== undefined){
10809                 a.points = {to: o.xy};
10810             }
10811             arguments.callee.anim = this.fxanim(a,
10812                 o, 'motion', .35, "easeOut", function(){
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819         /**
10820          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10821          * ending point of the effect.
10822          * Usage:
10823          *<pre><code>
10824 // default: slide the element downward while fading out
10825 el.ghost();
10826
10827 // custom: slide the element out to the right with a 2-second duration
10828 el.ghost('r', { duration: 2 });
10829
10830 // common config options shown with default values
10831 el.ghost('b', {
10832     easing: 'easeOut',
10833     duration: .5
10834     remove: false,
10835     useDisplay: false
10836 });
10837 </code></pre>
10838          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10839          * @param {Object} options (optional) Object literal with any of the Fx config options
10840          * @return {Roo.Element} The Element
10841          */
10842     ghost : function(anchor, o){
10843         var el = this.getFxEl();
10844         o = o || {};
10845
10846         el.queueFx(o, function(){
10847             anchor = anchor || "b";
10848
10849             // restore values after effect
10850             var r = this.getFxRestore();
10851             var w = this.getWidth(),
10852                 h = this.getHeight();
10853
10854             var st = this.dom.style;
10855
10856             var after = function(){
10857                 if(o.useDisplay){
10858                     el.setDisplayed(false);
10859                 }else{
10860                     el.hide();
10861                 }
10862
10863                 el.clearOpacity();
10864                 el.setPositioning(r.pos);
10865                 st.width = r.width;
10866                 st.height = r.height;
10867
10868                 el.afterFx(o);
10869             };
10870
10871             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10872             switch(anchor.toLowerCase()){
10873                 case "t":
10874                     pt.by = [0, -h];
10875                 break;
10876                 case "l":
10877                     pt.by = [-w, 0];
10878                 break;
10879                 case "r":
10880                     pt.by = [w, 0];
10881                 break;
10882                 case "b":
10883                     pt.by = [0, h];
10884                 break;
10885                 case "tl":
10886                     pt.by = [-w, -h];
10887                 break;
10888                 case "bl":
10889                     pt.by = [-w, h];
10890                 break;
10891                 case "br":
10892                     pt.by = [w, h];
10893                 break;
10894                 case "tr":
10895                     pt.by = [w, -h];
10896                 break;
10897             }
10898
10899             arguments.callee.anim = this.fxanim(a,
10900                 o,
10901                 'motion',
10902                 .5,
10903                 "easeOut", after);
10904         });
10905         return this;
10906     },
10907
10908         /**
10909          * Ensures that all effects queued after syncFx is called on the element are
10910          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10911          * @return {Roo.Element} The Element
10912          */
10913     syncFx : function(){
10914         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10915             block : false,
10916             concurrent : true,
10917             stopFx : false
10918         });
10919         return this;
10920     },
10921
10922         /**
10923          * Ensures that all effects queued after sequenceFx is called on the element are
10924          * run in sequence.  This is the opposite of {@link #syncFx}.
10925          * @return {Roo.Element} The Element
10926          */
10927     sequenceFx : function(){
10928         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10929             block : false,
10930             concurrent : false,
10931             stopFx : false
10932         });
10933         return this;
10934     },
10935
10936         /* @private */
10937     nextFx : function(){
10938         var ef = this.fxQueue[0];
10939         if(ef){
10940             ef.call(this);
10941         }
10942     },
10943
10944         /**
10945          * Returns true if the element has any effects actively running or queued, else returns false.
10946          * @return {Boolean} True if element has active effects, else false
10947          */
10948     hasActiveFx : function(){
10949         return this.fxQueue && this.fxQueue[0];
10950     },
10951
10952         /**
10953          * Stops any running effects and clears the element's internal effects queue if it contains
10954          * any additional effects that haven't started yet.
10955          * @return {Roo.Element} The Element
10956          */
10957     stopFx : function(){
10958         if(this.hasActiveFx()){
10959             var cur = this.fxQueue[0];
10960             if(cur && cur.anim && cur.anim.isAnimated()){
10961                 this.fxQueue = [cur]; // clear out others
10962                 cur.anim.stop(true);
10963             }
10964         }
10965         return this;
10966     },
10967
10968         /* @private */
10969     beforeFx : function(o){
10970         if(this.hasActiveFx() && !o.concurrent){
10971            if(o.stopFx){
10972                this.stopFx();
10973                return true;
10974            }
10975            return false;
10976         }
10977         return true;
10978     },
10979
10980         /**
10981          * Returns true if the element is currently blocking so that no other effect can be queued
10982          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10983          * used to ensure that an effect initiated by a user action runs to completion prior to the
10984          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10985          * @return {Boolean} True if blocking, else false
10986          */
10987     hasFxBlock : function(){
10988         var q = this.fxQueue;
10989         return q && q[0] && q[0].block;
10990     },
10991
10992         /* @private */
10993     queueFx : function(o, fn){
10994         if(!this.fxQueue){
10995             this.fxQueue = [];
10996         }
10997         if(!this.hasFxBlock()){
10998             Roo.applyIf(o, this.fxDefaults);
10999             if(!o.concurrent){
11000                 var run = this.beforeFx(o);
11001                 fn.block = o.block;
11002                 this.fxQueue.push(fn);
11003                 if(run){
11004                     this.nextFx();
11005                 }
11006             }else{
11007                 fn.call(this);
11008             }
11009         }
11010         return this;
11011     },
11012
11013         /* @private */
11014     fxWrap : function(pos, o, vis){
11015         var wrap;
11016         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11017             var wrapXY;
11018             if(o.fixPosition){
11019                 wrapXY = this.getXY();
11020             }
11021             var div = document.createElement("div");
11022             div.style.visibility = vis;
11023             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11024             wrap.setPositioning(pos);
11025             if(wrap.getStyle("position") == "static"){
11026                 wrap.position("relative");
11027             }
11028             this.clearPositioning('auto');
11029             wrap.clip();
11030             wrap.dom.appendChild(this.dom);
11031             if(wrapXY){
11032                 wrap.setXY(wrapXY);
11033             }
11034         }
11035         return wrap;
11036     },
11037
11038         /* @private */
11039     fxUnwrap : function(wrap, pos, o){
11040         this.clearPositioning();
11041         this.setPositioning(pos);
11042         if(!o.wrap){
11043             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11044             wrap.remove();
11045         }
11046     },
11047
11048         /* @private */
11049     getFxRestore : function(){
11050         var st = this.dom.style;
11051         return {pos: this.getPositioning(), width: st.width, height : st.height};
11052     },
11053
11054         /* @private */
11055     afterFx : function(o){
11056         if(o.afterStyle){
11057             this.applyStyles(o.afterStyle);
11058         }
11059         if(o.afterCls){
11060             this.addClass(o.afterCls);
11061         }
11062         if(o.remove === true){
11063             this.remove();
11064         }
11065         Roo.callback(o.callback, o.scope, [this]);
11066         if(!o.concurrent){
11067             this.fxQueue.shift();
11068             this.nextFx();
11069         }
11070     },
11071
11072         /* @private */
11073     getFxEl : function(){ // support for composite element fx
11074         return Roo.get(this.dom);
11075     },
11076
11077         /* @private */
11078     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11079         animType = animType || 'run';
11080         opt = opt || {};
11081         var anim = Roo.lib.Anim[animType](
11082             this.dom, args,
11083             (opt.duration || defaultDur) || .35,
11084             (opt.easing || defaultEase) || 'easeOut',
11085             function(){
11086                 Roo.callback(cb, this);
11087             },
11088             this
11089         );
11090         opt.anim = anim;
11091         return anim;
11092     }
11093 };
11094
11095 // backwords compat
11096 Roo.Fx.resize = Roo.Fx.scale;
11097
11098 //When included, Roo.Fx is automatically applied to Element so that all basic
11099 //effects are available directly via the Element API
11100 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11101  * Based on:
11102  * Ext JS Library 1.1.1
11103  * Copyright(c) 2006-2007, Ext JS, LLC.
11104  *
11105  * Originally Released Under LGPL - original licence link has changed is not relivant.
11106  *
11107  * Fork - LGPL
11108  * <script type="text/javascript">
11109  */
11110
11111
11112 /**
11113  * @class Roo.CompositeElement
11114  * Standard composite class. Creates a Roo.Element for every element in the collection.
11115  * <br><br>
11116  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11117  * actions will be performed on all the elements in this collection.</b>
11118  * <br><br>
11119  * All methods return <i>this</i> and can be chained.
11120  <pre><code>
11121  var els = Roo.select("#some-el div.some-class", true);
11122  // or select directly from an existing element
11123  var el = Roo.get('some-el');
11124  el.select('div.some-class', true);
11125
11126  els.setWidth(100); // all elements become 100 width
11127  els.hide(true); // all elements fade out and hide
11128  // or
11129  els.setWidth(100).hide(true);
11130  </code></pre>
11131  */
11132 Roo.CompositeElement = function(els){
11133     this.elements = [];
11134     this.addElements(els);
11135 };
11136 Roo.CompositeElement.prototype = {
11137     isComposite: true,
11138     addElements : function(els){
11139         if(!els) {
11140             return this;
11141         }
11142         if(typeof els == "string"){
11143             els = Roo.Element.selectorFunction(els);
11144         }
11145         var yels = this.elements;
11146         var index = yels.length-1;
11147         for(var i = 0, len = els.length; i < len; i++) {
11148                 yels[++index] = Roo.get(els[i]);
11149         }
11150         return this;
11151     },
11152
11153     /**
11154     * Clears this composite and adds the elements returned by the passed selector.
11155     * @param {String/Array} els A string CSS selector, an array of elements or an element
11156     * @return {CompositeElement} this
11157     */
11158     fill : function(els){
11159         this.elements = [];
11160         this.add(els);
11161         return this;
11162     },
11163
11164     /**
11165     * Filters this composite to only elements that match the passed selector.
11166     * @param {String} selector A string CSS selector
11167     * @param {Boolean} inverse return inverse filter (not matches)
11168     * @return {CompositeElement} this
11169     */
11170     filter : function(selector, inverse){
11171         var els = [];
11172         inverse = inverse || false;
11173         this.each(function(el){
11174             var match = inverse ? !el.is(selector) : el.is(selector);
11175             if(match){
11176                 els[els.length] = el.dom;
11177             }
11178         });
11179         this.fill(els);
11180         return this;
11181     },
11182
11183     invoke : function(fn, args){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186                 Roo.Element.prototype[fn].apply(els[i], args);
11187         }
11188         return this;
11189     },
11190     /**
11191     * Adds elements to this composite.
11192     * @param {String/Array} els A string CSS selector, an array of elements or an element
11193     * @return {CompositeElement} this
11194     */
11195     add : function(els){
11196         if(typeof els == "string"){
11197             this.addElements(Roo.Element.selectorFunction(els));
11198         }else if(els.length !== undefined){
11199             this.addElements(els);
11200         }else{
11201             this.addElements([els]);
11202         }
11203         return this;
11204     },
11205     /**
11206     * Calls the passed function passing (el, this, index) for each element in this composite.
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             if(fn.call(scope || els[i], els[i], this, i) === false) {
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     /**
11222      * Returns the Element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         return this.elements[index] || null;
11228     },
11229
11230     /**
11231      * Returns the first Element
11232      * @return {Roo.Element}
11233      */
11234     first : function(){
11235         return this.item(0);
11236     },
11237
11238     /**
11239      * Returns the last Element
11240      * @return {Roo.Element}
11241      */
11242     last : function(){
11243         return this.item(this.elements.length-1);
11244     },
11245
11246     /**
11247      * Returns the number of elements in this composite
11248      * @return Number
11249      */
11250     getCount : function(){
11251         return this.elements.length;
11252     },
11253
11254     /**
11255      * Returns true if this composite contains the passed element
11256      * @return Boolean
11257      */
11258     contains : function(el){
11259         return this.indexOf(el) !== -1;
11260     },
11261
11262     /**
11263      * Returns true if this composite contains the passed element
11264      * @return Boolean
11265      */
11266     indexOf : function(el){
11267         return this.elements.indexOf(Roo.get(el));
11268     },
11269
11270
11271     /**
11272     * Removes the specified element(s).
11273     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11274     * or an array of any of those.
11275     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11276     * @return {CompositeElement} this
11277     */
11278     removeElement : function(el, removeDom){
11279         if(el instanceof Array){
11280             for(var i = 0, len = el.length; i < len; i++){
11281                 this.removeElement(el[i]);
11282             }
11283             return this;
11284         }
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             if(removeDom){
11288                 var d = this.elements[index];
11289                 if(d.dom){
11290                     d.remove();
11291                 }else{
11292                     d.parentNode.removeChild(d);
11293                 }
11294             }
11295             this.elements.splice(index, 1);
11296         }
11297         return this;
11298     },
11299
11300     /**
11301     * Replaces the specified element with the passed element.
11302     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11303     * to replace.
11304     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11305     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11306     * @return {CompositeElement} this
11307     */
11308     replaceElement : function(el, replacement, domReplace){
11309         var index = typeof el == 'number' ? el : this.indexOf(el);
11310         if(index !== -1){
11311             if(domReplace){
11312                 this.elements[index].replaceWith(replacement);
11313             }else{
11314                 this.elements.splice(index, 1, Roo.get(replacement))
11315             }
11316         }
11317         return this;
11318     },
11319
11320     /**
11321      * Removes all elements.
11322      */
11323     clear : function(){
11324         this.elements = [];
11325     }
11326 };
11327 (function(){
11328     Roo.CompositeElement.createCall = function(proto, fnName){
11329         if(!proto[fnName]){
11330             proto[fnName] = function(){
11331                 return this.invoke(fnName, arguments);
11332             };
11333         }
11334     };
11335     for(var fnName in Roo.Element.prototype){
11336         if(typeof Roo.Element.prototype[fnName] == "function"){
11337             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11338         }
11339     };
11340 })();
11341 /*
11342  * Based on:
11343  * Ext JS Library 1.1.1
11344  * Copyright(c) 2006-2007, Ext JS, LLC.
11345  *
11346  * Originally Released Under LGPL - original licence link has changed is not relivant.
11347  *
11348  * Fork - LGPL
11349  * <script type="text/javascript">
11350  */
11351
11352 /**
11353  * @class Roo.CompositeElementLite
11354  * @extends Roo.CompositeElement
11355  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11356  <pre><code>
11357  var els = Roo.select("#some-el div.some-class");
11358  // or select directly from an existing element
11359  var el = Roo.get('some-el');
11360  el.select('div.some-class');
11361
11362  els.setWidth(100); // all elements become 100 width
11363  els.hide(true); // all elements fade out and hide
11364  // or
11365  els.setWidth(100).hide(true);
11366  </code></pre><br><br>
11367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11368  * actions will be performed on all the elements in this collection.</b>
11369  */
11370 Roo.CompositeElementLite = function(els){
11371     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11372     this.el = new Roo.Element.Flyweight();
11373 };
11374 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11375     addElements : function(els){
11376         if(els){
11377             if(els instanceof Array){
11378                 this.elements = this.elements.concat(els);
11379             }else{
11380                 var yels = this.elements;
11381                 var index = yels.length-1;
11382                 for(var i = 0, len = els.length; i < len; i++) {
11383                     yels[++index] = els[i];
11384                 }
11385             }
11386         }
11387         return this;
11388     },
11389     invoke : function(fn, args){
11390         var els = this.elements;
11391         var el = this.el;
11392         for(var i = 0, len = els.length; i < len; i++) {
11393             el.dom = els[i];
11394                 Roo.Element.prototype[fn].apply(el, args);
11395         }
11396         return this;
11397     },
11398     /**
11399      * Returns a flyweight Element of the dom element object at the specified index
11400      * @param {Number} index
11401      * @return {Roo.Element}
11402      */
11403     item : function(index){
11404         if(!this.elements[index]){
11405             return null;
11406         }
11407         this.el.dom = this.elements[index];
11408         return this.el;
11409     },
11410
11411     // fixes scope with flyweight
11412     addListener : function(eventName, handler, scope, opt){
11413         var els = this.elements;
11414         for(var i = 0, len = els.length; i < len; i++) {
11415             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11416         }
11417         return this;
11418     },
11419
11420     /**
11421     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11422     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11423     * a reference to the dom node, use el.dom.</b>
11424     * @param {Function} fn The function to call
11425     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11426     * @return {CompositeElement} this
11427     */
11428     each : function(fn, scope){
11429         var els = this.elements;
11430         var el = this.el;
11431         for(var i = 0, len = els.length; i < len; i++){
11432             el.dom = els[i];
11433                 if(fn.call(scope || el, el, this, i) === false){
11434                 break;
11435             }
11436         }
11437         return this;
11438     },
11439
11440     indexOf : function(el){
11441         return this.elements.indexOf(Roo.getDom(el));
11442     },
11443
11444     replaceElement : function(el, replacement, domReplace){
11445         var index = typeof el == 'number' ? el : this.indexOf(el);
11446         if(index !== -1){
11447             replacement = Roo.getDom(replacement);
11448             if(domReplace){
11449                 var d = this.elements[index];
11450                 d.parentNode.insertBefore(replacement, d);
11451                 d.parentNode.removeChild(d);
11452             }
11453             this.elements.splice(index, 1, replacement);
11454         }
11455         return this;
11456     }
11457 });
11458 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11459
11460 /*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471  
11472
11473 /**
11474  * @class Roo.data.Connection
11475  * @extends Roo.util.Observable
11476  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11477  * either to a configured URL, or to a URL specified at request time. 
11478  * 
11479  * Requests made by this class are asynchronous, and will return immediately. No data from
11480  * the server will be available to the statement immediately following the {@link #request} call.
11481  * To process returned data, use a callback in the request options object, or an event listener.
11482  * 
11483  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11484  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11485  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11486  * property and, if present, the IFRAME's XML document as the responseXML property.
11487  * 
11488  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11489  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11490  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11491  * standard DOM methods.
11492  * @constructor
11493  * @param {Object} config a configuration object.
11494  */
11495 Roo.data.Connection = function(config){
11496     Roo.apply(this, config);
11497     this.addEvents({
11498         /**
11499          * @event beforerequest
11500          * Fires before a network request is made to retrieve a data object.
11501          * @param {Connection} conn This Connection object.
11502          * @param {Object} options The options config object passed to the {@link #request} method.
11503          */
11504         "beforerequest" : true,
11505         /**
11506          * @event requestcomplete
11507          * Fires if the request was successfully completed.
11508          * @param {Connection} conn This Connection object.
11509          * @param {Object} response The XHR object containing the response data.
11510          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11511          * @param {Object} options The options config object passed to the {@link #request} method.
11512          */
11513         "requestcomplete" : true,
11514         /**
11515          * @event requestexception
11516          * Fires if an error HTTP status was returned from the server.
11517          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11518          * @param {Connection} conn This Connection object.
11519          * @param {Object} response The XHR object containing the response data.
11520          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11521          * @param {Object} options The options config object passed to the {@link #request} method.
11522          */
11523         "requestexception" : true
11524     });
11525     Roo.data.Connection.superclass.constructor.call(this);
11526 };
11527
11528 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11529     /**
11530      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11538      *  to each request made by this object. (defaults to undefined)
11539      */
11540     /**
11541      * @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)
11542      */
11543     /**
11544      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11545      */
11546     timeout : 30000,
11547     /**
11548      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort:false,
11552
11553     /**
11554      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11555      * @type Boolean
11556      */
11557     disableCaching: true,
11558
11559     /**
11560      * Sends an HTTP request to a remote server.
11561      * @param {Object} options An object which may contain the following properties:<ul>
11562      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11563      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11564      * request, a url encoded string or a function to call to get either.</li>
11565      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11566      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11567      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11568      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11569      * <li>options {Object} The parameter to the request call.</li>
11570      * <li>success {Boolean} True if the request succeeded.</li>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * </ul></li>
11573      * <li><b>success</b> {Function} (Optional) The function to be called upon success 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>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11579      * The callback is passed the following parameters:<ul>
11580      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11581      * <li>options {Object} The parameter to the request call.</li>
11582      * </ul></li>
11583      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11584      * for the callback function. Defaults to the browser window.</li>
11585      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11586      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11587      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11588      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11589      * params for the post data. Any params will be appended to the URL.</li>
11590      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11591      * </ul>
11592      * @return {Number} transactionId
11593      */
11594     request : function(o){
11595         if(this.fireEvent("beforerequest", this, o) !== false){
11596             var p = o.params;
11597
11598             if(typeof p == "function"){
11599                 p = p.call(o.scope||window, o);
11600             }
11601             if(typeof p == "object"){
11602                 p = Roo.urlEncode(o.params);
11603             }
11604             if(this.extraParams){
11605                 var extras = Roo.urlEncode(this.extraParams);
11606                 p = p ? (p + '&' + extras) : extras;
11607             }
11608
11609             var url = o.url || this.url;
11610             if(typeof url == 'function'){
11611                 url = url.call(o.scope||window, o);
11612             }
11613
11614             if(o.form){
11615                 var form = Roo.getDom(o.form);
11616                 url = url || form.action;
11617
11618                 var enctype = form.getAttribute("enctype");
11619                 
11620                 if (o.formData) {
11621                     return this.doFormDataUpload(o,p,url);
11622                 }
11623                 
11624                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11625                     return this.doFormUpload(o, p, url);
11626                 }
11627                 var f = Roo.lib.Ajax.serializeForm(form);
11628                 p = p ? (p + '&' + f) : f;
11629             }
11630
11631             var hs = o.headers;
11632             if(this.defaultHeaders){
11633                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11634                 if(!o.headers){
11635                     o.headers = hs;
11636                 }
11637             }
11638
11639             var cb = {
11640                 success: this.handleResponse,
11641                 failure: this.handleFailure,
11642                 scope: this,
11643                 argument: {options: o},
11644                 timeout : o.timeout || this.timeout
11645             };
11646
11647             var method = o.method||this.method||(p ? "POST" : "GET");
11648
11649             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11650                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11651             }
11652
11653             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11654                 if(o.autoAbort){
11655                     this.abort();
11656                 }
11657             }else if(this.autoAbort !== false){
11658                 this.abort();
11659             }
11660
11661             if((method == 'GET' && p) || o.xmlData){
11662                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11663                 p = '';
11664             }
11665             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11666             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11667             Roo.lib.Ajax.useDefaultHeader == true;
11668             return this.transId;
11669         }else{
11670             Roo.callback(o.callback, o.scope, [o, null, null]);
11671             return null;
11672         }
11673     },
11674
11675     /**
11676      * Determine whether this object has a request outstanding.
11677      * @param {Number} transactionId (Optional) defaults to the last transaction
11678      * @return {Boolean} True if there is an outstanding request.
11679      */
11680     isLoading : function(transId){
11681         if(transId){
11682             return Roo.lib.Ajax.isCallInProgress(transId);
11683         }else{
11684             return this.transId ? true : false;
11685         }
11686     },
11687
11688     /**
11689      * Aborts any outstanding request.
11690      * @param {Number} transactionId (Optional) defaults to the last transaction
11691      */
11692     abort : function(transId){
11693         if(transId || this.isLoading()){
11694             Roo.lib.Ajax.abort(transId || this.transId);
11695         }
11696     },
11697
11698     // private
11699     handleResponse : function(response){
11700         this.transId = false;
11701         var options = response.argument.options;
11702         response.argument = options ? options.argument : null;
11703         this.fireEvent("requestcomplete", this, response, options);
11704         Roo.callback(options.success, options.scope, [response, options]);
11705         Roo.callback(options.callback, options.scope, [options, true, response]);
11706     },
11707
11708     // private
11709     handleFailure : function(response, e){
11710         this.transId = false;
11711         var options = response.argument.options;
11712         response.argument = options ? options.argument : null;
11713         this.fireEvent("requestexception", this, response, options, e);
11714         Roo.callback(options.failure, options.scope, [response, options]);
11715         Roo.callback(options.callback, options.scope, [options, false, response]);
11716     },
11717
11718     // private
11719     doFormUpload : function(o, ps, url){
11720         var id = Roo.id();
11721         var frame = document.createElement('iframe');
11722         frame.id = id;
11723         frame.name = id;
11724         frame.className = 'x-hidden';
11725         if(Roo.isIE){
11726             frame.src = Roo.SSL_SECURE_URL;
11727         }
11728         document.body.appendChild(frame);
11729
11730         if(Roo.isIE){
11731            document.frames[id].name = id;
11732         }
11733
11734         var form = Roo.getDom(o.form);
11735         form.target = id;
11736         form.method = 'POST';
11737         form.enctype = form.encoding = 'multipart/form-data';
11738         if(url){
11739             form.action = url;
11740         }
11741
11742         var hiddens, hd;
11743         if(ps){ // add dynamic params
11744             hiddens = [];
11745             ps = Roo.urlDecode(ps, false);
11746             for(var k in ps){
11747                 if(ps.hasOwnProperty(k)){
11748                     hd = document.createElement('input');
11749                     hd.type = 'hidden';
11750                     hd.name = k;
11751                     hd.value = ps[k];
11752                     form.appendChild(hd);
11753                     hiddens.push(hd);
11754                 }
11755             }
11756         }
11757
11758         function cb(){
11759             var r = {  // bogus response object
11760                 responseText : '',
11761                 responseXML : null
11762             };
11763
11764             r.argument = o ? o.argument : null;
11765
11766             try { //
11767                 var doc;
11768                 if(Roo.isIE){
11769                     doc = frame.contentWindow.document;
11770                 }else {
11771                     doc = (frame.contentDocument || window.frames[id].document);
11772                 }
11773                 if(doc && doc.body){
11774                     r.responseText = doc.body.innerHTML;
11775                 }
11776                 if(doc && doc.XMLDocument){
11777                     r.responseXML = doc.XMLDocument;
11778                 }else {
11779                     r.responseXML = doc;
11780                 }
11781             }
11782             catch(e) {
11783                 // ignore
11784             }
11785
11786             Roo.EventManager.removeListener(frame, 'load', cb, this);
11787
11788             this.fireEvent("requestcomplete", this, r, o);
11789             Roo.callback(o.success, o.scope, [r, o]);
11790             Roo.callback(o.callback, o.scope, [o, true, r]);
11791
11792             setTimeout(function(){document.body.removeChild(frame);}, 100);
11793         }
11794
11795         Roo.EventManager.on(frame, 'load', cb, this);
11796         form.submit();
11797
11798         if(hiddens){ // remove dynamic params
11799             for(var i = 0, len = hiddens.length; i < len; i++){
11800                 form.removeChild(hiddens[i]);
11801             }
11802         }
11803     },
11804     // this is a 'formdata version???'
11805     
11806     
11807     doFormDataUpload : function(o, ps, url)
11808     {
11809         var form = Roo.getDom(o.form);
11810         form.enctype = form.encoding = 'multipart/form-data';
11811         var formData = o.formData === true ? new FormData(form) : o.formData;
11812       
11813         var cb = {
11814             success: this.handleResponse,
11815             failure: this.handleFailure,
11816             scope: this,
11817             argument: {options: o},
11818             timeout : o.timeout || this.timeout
11819         };
11820  
11821         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11822             if(o.autoAbort){
11823                 this.abort();
11824             }
11825         }else if(this.autoAbort !== false){
11826             this.abort();
11827         }
11828
11829         //Roo.lib.Ajax.defaultPostHeader = null;
11830         Roo.lib.Ajax.useDefaultHeader = false;
11831         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11832         Roo.lib.Ajax.useDefaultHeader = true;
11833  
11834          
11835     }
11836     
11837 });
11838 /*
11839  * Based on:
11840  * Ext JS Library 1.1.1
11841  * Copyright(c) 2006-2007, Ext JS, LLC.
11842  *
11843  * Originally Released Under LGPL - original licence link has changed is not relivant.
11844  *
11845  * Fork - LGPL
11846  * <script type="text/javascript">
11847  */
11848  
11849 /**
11850  * Global Ajax request class.
11851  * 
11852  * @class Roo.Ajax
11853  * @extends Roo.data.Connection
11854  * @static
11855  * 
11856  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11857  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11858  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11859  * @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)
11860  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11861  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11862  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11863  */
11864 Roo.Ajax = new Roo.data.Connection({
11865     // fix up the docs
11866     /**
11867      * @scope Roo.Ajax
11868      * @type {Boolear} 
11869      */
11870     autoAbort : false,
11871
11872     /**
11873      * Serialize the passed form into a url encoded string
11874      * @scope Roo.Ajax
11875      * @param {String/HTMLElement} form
11876      * @return {String}
11877      */
11878     serializeForm : function(form){
11879         return Roo.lib.Ajax.serializeForm(form);
11880     }
11881 });/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892  
11893 /**
11894  * @class Roo.UpdateManager
11895  * @extends Roo.util.Observable
11896  * Provides AJAX-style update for Element object.<br><br>
11897  * Usage:<br>
11898  * <pre><code>
11899  * // Get it from a Roo.Element object
11900  * var el = Roo.get("foo");
11901  * var mgr = el.getUpdateManager();
11902  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11903  * ...
11904  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11905  * <br>
11906  * // or directly (returns the same UpdateManager instance)
11907  * var mgr = new Roo.UpdateManager("myElementId");
11908  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11909  * mgr.on("update", myFcnNeedsToKnow);
11910  * <br>
11911    // short handed call directly from the element object
11912    Roo.get("foo").load({
11913         url: "bar.php",
11914         scripts:true,
11915         params: "for=bar",
11916         text: "Loading Foo..."
11917    });
11918  * </code></pre>
11919  * @constructor
11920  * Create new UpdateManager directly.
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @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).
11923  */
11924 Roo.UpdateManager = function(el, forceNew){
11925     el = Roo.get(el);
11926     if(!forceNew && el.updateManager){
11927         return el.updateManager;
11928     }
11929     /**
11930      * The Element object
11931      * @type Roo.Element
11932      */
11933     this.el = el;
11934     /**
11935      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11936      * @type String
11937      */
11938     this.defaultUrl = null;
11939
11940     this.addEvents({
11941         /**
11942          * @event beforeupdate
11943          * Fired before an update is made, return false from your handler and the update is cancelled.
11944          * @param {Roo.Element} el
11945          * @param {String/Object/Function} url
11946          * @param {String/Object} params
11947          */
11948         "beforeupdate": true,
11949         /**
11950          * @event update
11951          * Fired after successful update is made.
11952          * @param {Roo.Element} el
11953          * @param {Object} oResponseObject The response Object
11954          */
11955         "update": true,
11956         /**
11957          * @event failure
11958          * Fired on update failure.
11959          * @param {Roo.Element} el
11960          * @param {Object} oResponseObject The response Object
11961          */
11962         "failure": true
11963     });
11964     var d = Roo.UpdateManager.defaults;
11965     /**
11966      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11967      * @type String
11968      */
11969     this.sslBlankUrl = d.sslBlankUrl;
11970     /**
11971      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11972      * @type Boolean
11973      */
11974     this.disableCaching = d.disableCaching;
11975     /**
11976      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11977      * @type String
11978      */
11979     this.indicatorText = d.indicatorText;
11980     /**
11981      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11982      * @type String
11983      */
11984     this.showLoadIndicator = d.showLoadIndicator;
11985     /**
11986      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11987      * @type Number
11988      */
11989     this.timeout = d.timeout;
11990
11991     /**
11992      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11993      * @type Boolean
11994      */
11995     this.loadScripts = d.loadScripts;
11996
11997     /**
11998      * Transaction object of current executing transaction
11999      */
12000     this.transaction = null;
12001
12002     /**
12003      * @private
12004      */
12005     this.autoRefreshProcId = null;
12006     /**
12007      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12008      * @type Function
12009      */
12010     this.refreshDelegate = this.refresh.createDelegate(this);
12011     /**
12012      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12013      * @type Function
12014      */
12015     this.updateDelegate = this.update.createDelegate(this);
12016     /**
12017      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12018      * @type Function
12019      */
12020     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12021     /**
12022      * @private
12023      */
12024     this.successDelegate = this.processSuccess.createDelegate(this);
12025     /**
12026      * @private
12027      */
12028     this.failureDelegate = this.processFailure.createDelegate(this);
12029
12030     if(!this.renderer){
12031      /**
12032       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12033       */
12034     this.renderer = new Roo.UpdateManager.BasicRenderer();
12035     }
12036     
12037     Roo.UpdateManager.superclass.constructor.call(this);
12038 };
12039
12040 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12041     /**
12042      * Get the Element this UpdateManager is bound to
12043      * @return {Roo.Element} The element
12044      */
12045     getEl : function(){
12046         return this.el;
12047     },
12048     /**
12049      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12050      * @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:
12051 <pre><code>
12052 um.update({<br/>
12053     url: "your-url.php",<br/>
12054     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12055     callback: yourFunction,<br/>
12056     scope: yourObject, //(optional scope)  <br/>
12057     discardUrl: false, <br/>
12058     nocache: false,<br/>
12059     text: "Loading...",<br/>
12060     timeout: 30,<br/>
12061     scripts: false<br/>
12062 });
12063 </code></pre>
12064      * The only required property is url. The optional properties nocache, text and scripts
12065      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12066      * @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}
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12068      * @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.
12069      */
12070     update : function(url, params, callback, discardUrl){
12071         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12072             var method = this.method,
12073                 cfg;
12074             if(typeof url == "object"){ // must be config object
12075                 cfg = url;
12076                 url = cfg.url;
12077                 params = params || cfg.params;
12078                 callback = callback || cfg.callback;
12079                 discardUrl = discardUrl || cfg.discardUrl;
12080                 if(callback && cfg.scope){
12081                     callback = callback.createDelegate(cfg.scope);
12082                 }
12083                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12084                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12085                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12086                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12087                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12088             }
12089             this.showLoading();
12090             if(!discardUrl){
12091                 this.defaultUrl = url;
12092             }
12093             if(typeof url == "function"){
12094                 url = url.call(this);
12095             }
12096
12097             method = method || (params ? "POST" : "GET");
12098             if(method == "GET"){
12099                 url = this.prepareUrl(url);
12100             }
12101
12102             var o = Roo.apply(cfg ||{}, {
12103                 url : url,
12104                 params: params,
12105                 success: this.successDelegate,
12106                 failure: this.failureDelegate,
12107                 callback: undefined,
12108                 timeout: (this.timeout*1000),
12109                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12110             });
12111             Roo.log("updated manager called with timeout of " + o.timeout);
12112             this.transaction = Roo.Ajax.request(o);
12113         }
12114     },
12115
12116     /**
12117      * 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.
12118      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12119      * @param {String/HTMLElement} form The form Id or form element
12120      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12121      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12122      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12123      */
12124     formUpdate : function(form, url, reset, callback){
12125         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12126             if(typeof url == "function"){
12127                 url = url.call(this);
12128             }
12129             form = Roo.getDom(form);
12130             this.transaction = Roo.Ajax.request({
12131                 form: form,
12132                 url:url,
12133                 success: this.successDelegate,
12134                 failure: this.failureDelegate,
12135                 timeout: (this.timeout*1000),
12136                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12137             });
12138             this.showLoading.defer(1, this);
12139         }
12140     },
12141
12142     /**
12143      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12144      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12145      */
12146     refresh : function(callback){
12147         if(this.defaultUrl == null){
12148             return;
12149         }
12150         this.update(this.defaultUrl, null, callback, true);
12151     },
12152
12153     /**
12154      * Set this element to auto refresh.
12155      * @param {Number} interval How often to update (in seconds).
12156      * @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)
12157      * @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}
12158      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12159      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12160      */
12161     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12162         if(refreshNow){
12163             this.update(url || this.defaultUrl, params, callback, true);
12164         }
12165         if(this.autoRefreshProcId){
12166             clearInterval(this.autoRefreshProcId);
12167         }
12168         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12169     },
12170
12171     /**
12172      * Stop auto refresh on this element.
12173      */
12174      stopAutoRefresh : function(){
12175         if(this.autoRefreshProcId){
12176             clearInterval(this.autoRefreshProcId);
12177             delete this.autoRefreshProcId;
12178         }
12179     },
12180
12181     isAutoRefreshing : function(){
12182        return this.autoRefreshProcId ? true : false;
12183     },
12184     /**
12185      * Called to update the element to "Loading" state. Override to perform custom action.
12186      */
12187     showLoading : function(){
12188         if(this.showLoadIndicator){
12189             this.el.update(this.indicatorText);
12190         }
12191     },
12192
12193     /**
12194      * Adds unique parameter to query string if disableCaching = true
12195      * @private
12196      */
12197     prepareUrl : function(url){
12198         if(this.disableCaching){
12199             var append = "_dc=" + (new Date().getTime());
12200             if(url.indexOf("?") !== -1){
12201                 url += "&" + append;
12202             }else{
12203                 url += "?" + append;
12204             }
12205         }
12206         return url;
12207     },
12208
12209     /**
12210      * @private
12211      */
12212     processSuccess : function(response){
12213         this.transaction = null;
12214         if(response.argument.form && response.argument.reset){
12215             try{ // put in try/catch since some older FF releases had problems with this
12216                 response.argument.form.reset();
12217             }catch(e){}
12218         }
12219         if(this.loadScripts){
12220             this.renderer.render(this.el, response, this,
12221                 this.updateComplete.createDelegate(this, [response]));
12222         }else{
12223             this.renderer.render(this.el, response, this);
12224             this.updateComplete(response);
12225         }
12226     },
12227
12228     updateComplete : function(response){
12229         this.fireEvent("update", this.el, response);
12230         if(typeof response.argument.callback == "function"){
12231             response.argument.callback(this.el, true, response);
12232         }
12233     },
12234
12235     /**
12236      * @private
12237      */
12238     processFailure : function(response){
12239         this.transaction = null;
12240         this.fireEvent("failure", this.el, response);
12241         if(typeof response.argument.callback == "function"){
12242             response.argument.callback(this.el, false, response);
12243         }
12244     },
12245
12246     /**
12247      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12248      * @param {Object} renderer The object implementing the render() method
12249      */
12250     setRenderer : function(renderer){
12251         this.renderer = renderer;
12252     },
12253
12254     getRenderer : function(){
12255        return this.renderer;
12256     },
12257
12258     /**
12259      * Set the defaultUrl used for updates
12260      * @param {String/Function} defaultUrl The url or a function to call to get the url
12261      */
12262     setDefaultUrl : function(defaultUrl){
12263         this.defaultUrl = defaultUrl;
12264     },
12265
12266     /**
12267      * Aborts the executing transaction
12268      */
12269     abort : function(){
12270         if(this.transaction){
12271             Roo.Ajax.abort(this.transaction);
12272         }
12273     },
12274
12275     /**
12276      * Returns true if an update is in progress
12277      * @return {Boolean}
12278      */
12279     isUpdating : function(){
12280         if(this.transaction){
12281             return Roo.Ajax.isLoading(this.transaction);
12282         }
12283         return false;
12284     }
12285 });
12286
12287 /**
12288  * @class Roo.UpdateManager.defaults
12289  * @static (not really - but it helps the doc tool)
12290  * The defaults collection enables customizing the default properties of UpdateManager
12291  */
12292    Roo.UpdateManager.defaults = {
12293        /**
12294          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12295          * @type Number
12296          */
12297          timeout : 30,
12298
12299          /**
12300          * True to process scripts by default (Defaults to false).
12301          * @type Boolean
12302          */
12303         loadScripts : false,
12304
12305         /**
12306         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12307         * @type String
12308         */
12309         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12310         /**
12311          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12312          * @type Boolean
12313          */
12314         disableCaching : false,
12315         /**
12316          * Whether to show indicatorText when loading (Defaults to true).
12317          * @type Boolean
12318          */
12319         showLoadIndicator : true,
12320         /**
12321          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12322          * @type String
12323          */
12324         indicatorText : '<div class="loading-indicator">Loading...</div>'
12325    };
12326
12327 /**
12328  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12329  *Usage:
12330  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12331  * @param {String/HTMLElement/Roo.Element} el The element to update
12332  * @param {String} url The url
12333  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12334  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12335  * @static
12336  * @deprecated
12337  * @member Roo.UpdateManager
12338  */
12339 Roo.UpdateManager.updateElement = function(el, url, params, options){
12340     var um = Roo.get(el, true).getUpdateManager();
12341     Roo.apply(um, options);
12342     um.update(url, params, options ? options.callback : null);
12343 };
12344 // alias for backwards compat
12345 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12346 /**
12347  * @class Roo.UpdateManager.BasicRenderer
12348  * Default Content renderer. Updates the elements innerHTML with the responseText.
12349  */
12350 Roo.UpdateManager.BasicRenderer = function(){};
12351
12352 Roo.UpdateManager.BasicRenderer.prototype = {
12353     /**
12354      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12355      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12356      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12357      * @param {Roo.Element} el The element being rendered
12358      * @param {Object} response The YUI Connect response object
12359      * @param {UpdateManager} updateManager The calling update manager
12360      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12361      */
12362      render : function(el, response, updateManager, callback){
12363         el.update(response.responseText, updateManager.loadScripts, callback);
12364     }
12365 };
12366 /*
12367  * Based on:
12368  * Roo JS
12369  * (c)) Alan Knowles
12370  * Licence : LGPL
12371  */
12372
12373
12374 /**
12375  * @class Roo.DomTemplate
12376  * @extends Roo.Template
12377  * An effort at a dom based template engine..
12378  *
12379  * Similar to XTemplate, except it uses dom parsing to create the template..
12380  *
12381  * Supported features:
12382  *
12383  *  Tags:
12384
12385 <pre><code>
12386       {a_variable} - output encoded.
12387       {a_variable.format:("Y-m-d")} - call a method on the variable
12388       {a_variable:raw} - unencoded output
12389       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12390       {a_variable:this.method_on_template(...)} - call a method on the template object.
12391  
12392 </code></pre>
12393  *  The tpl tag:
12394 <pre><code>
12395         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12396         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12397         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12398         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12399   
12400 </code></pre>
12401  *      
12402  */
12403 Roo.DomTemplate = function()
12404 {
12405      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12406      if (this.html) {
12407         this.compile();
12408      }
12409 };
12410
12411
12412 Roo.extend(Roo.DomTemplate, Roo.Template, {
12413     /**
12414      * id counter for sub templates.
12415      */
12416     id : 0,
12417     /**
12418      * flag to indicate if dom parser is inside a pre,
12419      * it will strip whitespace if not.
12420      */
12421     inPre : false,
12422     
12423     /**
12424      * The various sub templates
12425      */
12426     tpls : false,
12427     
12428     
12429     
12430     /**
12431      *
12432      * basic tag replacing syntax
12433      * WORD:WORD()
12434      *
12435      * // you can fake an object call by doing this
12436      *  x.t:(test,tesT) 
12437      * 
12438      */
12439     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12440     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12441     
12442     iterChild : function (node, method) {
12443         
12444         var oldPre = this.inPre;
12445         if (node.tagName == 'PRE') {
12446             this.inPre = true;
12447         }
12448         for( var i = 0; i < node.childNodes.length; i++) {
12449             method.call(this, node.childNodes[i]);
12450         }
12451         this.inPre = oldPre;
12452     },
12453     
12454     
12455     
12456     /**
12457      * compile the template
12458      *
12459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12460      *
12461      */
12462     compile: function()
12463     {
12464         var s = this.html;
12465         
12466         // covert the html into DOM...
12467         var doc = false;
12468         var div =false;
12469         try {
12470             doc = document.implementation.createHTMLDocument("");
12471             doc.documentElement.innerHTML =   this.html  ;
12472             div = doc.documentElement;
12473         } catch (e) {
12474             // old IE... - nasty -- it causes all sorts of issues.. with
12475             // images getting pulled from server..
12476             div = document.createElement('div');
12477             div.innerHTML = this.html;
12478         }
12479         //doc.documentElement.innerHTML = htmlBody
12480          
12481         
12482         
12483         this.tpls = [];
12484         var _t = this;
12485         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12486         
12487         var tpls = this.tpls;
12488         
12489         // create a top level template from the snippet..
12490         
12491         //Roo.log(div.innerHTML);
12492         
12493         var tpl = {
12494             uid : 'master',
12495             id : this.id++,
12496             attr : false,
12497             value : false,
12498             body : div.innerHTML,
12499             
12500             forCall : false,
12501             execCall : false,
12502             dom : div,
12503             isTop : true
12504             
12505         };
12506         tpls.unshift(tpl);
12507         
12508         
12509         // compile them...
12510         this.tpls = [];
12511         Roo.each(tpls, function(tp){
12512             this.compileTpl(tp);
12513             this.tpls[tp.id] = tp;
12514         }, this);
12515         
12516         this.master = tpls[0];
12517         return this;
12518         
12519         
12520     },
12521     
12522     compileNode : function(node, istop) {
12523         // test for
12524         //Roo.log(node);
12525         
12526         
12527         // skip anything not a tag..
12528         if (node.nodeType != 1) {
12529             if (node.nodeType == 3 && !this.inPre) {
12530                 // reduce white space..
12531                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12532                 
12533             }
12534             return;
12535         }
12536         
12537         var tpl = {
12538             uid : false,
12539             id : false,
12540             attr : false,
12541             value : false,
12542             body : '',
12543             
12544             forCall : false,
12545             execCall : false,
12546             dom : false,
12547             isTop : istop
12548             
12549             
12550         };
12551         
12552         
12553         switch(true) {
12554             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12555             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12556             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12557             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12558             // no default..
12559         }
12560         
12561         
12562         if (!tpl.attr) {
12563             // just itterate children..
12564             this.iterChild(node,this.compileNode);
12565             return;
12566         }
12567         tpl.uid = this.id++;
12568         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12569         node.removeAttribute('roo-'+ tpl.attr);
12570         if (tpl.attr != 'name') {
12571             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12572             node.parentNode.replaceChild(placeholder,  node);
12573         } else {
12574             
12575             var placeholder =  document.createElement('span');
12576             placeholder.className = 'roo-tpl-' + tpl.value;
12577             node.parentNode.replaceChild(placeholder,  node);
12578         }
12579         
12580         // parent now sees '{domtplXXXX}
12581         this.iterChild(node,this.compileNode);
12582         
12583         // we should now have node body...
12584         var div = document.createElement('div');
12585         div.appendChild(node);
12586         tpl.dom = node;
12587         // this has the unfortunate side effect of converting tagged attributes
12588         // eg. href="{...}" into %7C...%7D
12589         // this has been fixed by searching for those combo's although it's a bit hacky..
12590         
12591         
12592         tpl.body = div.innerHTML;
12593         
12594         
12595          
12596         tpl.id = tpl.uid;
12597         switch(tpl.attr) {
12598             case 'for' :
12599                 switch (tpl.value) {
12600                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12601                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12602                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12603                 }
12604                 break;
12605             
12606             case 'exec':
12607                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12608                 break;
12609             
12610             case 'if':     
12611                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12612                 break;
12613             
12614             case 'name':
12615                 tpl.id  = tpl.value; // replace non characters???
12616                 break;
12617             
12618         }
12619         
12620         
12621         this.tpls.push(tpl);
12622         
12623         
12624         
12625     },
12626     
12627     
12628     
12629     
12630     /**
12631      * Compile a segment of the template into a 'sub-template'
12632      *
12633      * 
12634      * 
12635      *
12636      */
12637     compileTpl : function(tpl)
12638     {
12639         var fm = Roo.util.Format;
12640         var useF = this.disableFormats !== true;
12641         
12642         var sep = Roo.isGecko ? "+\n" : ",\n";
12643         
12644         var undef = function(str) {
12645             Roo.debug && Roo.log("Property not found :"  + str);
12646             return '';
12647         };
12648           
12649         //Roo.log(tpl.body);
12650         
12651         
12652         
12653         var fn = function(m, lbrace, name, format, args)
12654         {
12655             //Roo.log("ARGS");
12656             //Roo.log(arguments);
12657             args = args ? args.replace(/\\'/g,"'") : args;
12658             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12659             if (typeof(format) == 'undefined') {
12660                 format =  'htmlEncode'; 
12661             }
12662             if (format == 'raw' ) {
12663                 format = false;
12664             }
12665             
12666             if(name.substr(0, 6) == 'domtpl'){
12667                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12668             }
12669             
12670             // build an array of options to determine if value is undefined..
12671             
12672             // basically get 'xxxx.yyyy' then do
12673             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12674             //    (function () { Roo.log("Property not found"); return ''; })() :
12675             //    ......
12676             
12677             var udef_ar = [];
12678             var lookfor = '';
12679             Roo.each(name.split('.'), function(st) {
12680                 lookfor += (lookfor.length ? '.': '') + st;
12681                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12682             });
12683             
12684             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12685             
12686             
12687             if(format && useF){
12688                 
12689                 args = args ? ',' + args : "";
12690                  
12691                 if(format.substr(0, 5) != "this."){
12692                     format = "fm." + format + '(';
12693                 }else{
12694                     format = 'this.call("'+ format.substr(5) + '", ';
12695                     args = ", values";
12696                 }
12697                 
12698                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12699             }
12700              
12701             if (args && args.length) {
12702                 // called with xxyx.yuu:(test,test)
12703                 // change to ()
12704                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12705             }
12706             // raw.. - :raw modifier..
12707             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12708             
12709         };
12710         var body;
12711         // branched to use + in gecko and [].join() in others
12712         if(Roo.isGecko){
12713             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12714                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12715                     "';};};";
12716         }else{
12717             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12718             body.push(tpl.body.replace(/(\r\n|\n)/g,
12719                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12720             body.push("'].join('');};};");
12721             body = body.join('');
12722         }
12723         
12724         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12725        
12726         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12727         eval(body);
12728         
12729         return this;
12730     },
12731      
12732     /**
12733      * same as applyTemplate, except it's done to one of the subTemplates
12734      * when using named templates, you can do:
12735      *
12736      * var str = pl.applySubTemplate('your-name', values);
12737      *
12738      * 
12739      * @param {Number} id of the template
12740      * @param {Object} values to apply to template
12741      * @param {Object} parent (normaly the instance of this object)
12742      */
12743     applySubTemplate : function(id, values, parent)
12744     {
12745         
12746         
12747         var t = this.tpls[id];
12748         
12749         
12750         try { 
12751             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12752                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12753                 return '';
12754             }
12755         } catch(e) {
12756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12757             Roo.log(values);
12758           
12759             return '';
12760         }
12761         try { 
12762             
12763             if(t.execCall && t.execCall.call(this, values, parent)){
12764                 return '';
12765             }
12766         } catch(e) {
12767             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12768             Roo.log(values);
12769             return '';
12770         }
12771         
12772         try {
12773             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12774             parent = t.target ? values : parent;
12775             if(t.forCall && vs instanceof Array){
12776                 var buf = [];
12777                 for(var i = 0, len = vs.length; i < len; i++){
12778                     try {
12779                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12780                     } catch (e) {
12781                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12782                         Roo.log(e.body);
12783                         //Roo.log(t.compiled);
12784                         Roo.log(vs[i]);
12785                     }   
12786                 }
12787                 return buf.join('');
12788             }
12789         } catch (e) {
12790             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12791             Roo.log(values);
12792             return '';
12793         }
12794         try {
12795             return t.compiled.call(this, vs, parent);
12796         } catch (e) {
12797             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12798             Roo.log(e.body);
12799             //Roo.log(t.compiled);
12800             Roo.log(values);
12801             return '';
12802         }
12803     },
12804
12805    
12806
12807     applyTemplate : function(values){
12808         return this.master.compiled.call(this, values, {});
12809         //var s = this.subs;
12810     },
12811
12812     apply : function(){
12813         return this.applyTemplate.apply(this, arguments);
12814     }
12815
12816  });
12817
12818 Roo.DomTemplate.from = function(el){
12819     el = Roo.getDom(el);
12820     return new Roo.Domtemplate(el.value || el.innerHTML);
12821 };/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832 /**
12833  * @class Roo.util.DelayedTask
12834  * Provides a convenient method of performing setTimeout where a new
12835  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12836  * You can use this class to buffer
12837  * the keypress events for a certain number of milliseconds, and perform only if they stop
12838  * for that amount of time.
12839  * @constructor The parameters to this constructor serve as defaults and are not required.
12840  * @param {Function} fn (optional) The default function to timeout
12841  * @param {Object} scope (optional) The default scope of that timeout
12842  * @param {Array} args (optional) The default Array of arguments
12843  */
12844 Roo.util.DelayedTask = function(fn, scope, args){
12845     var id = null, d, t;
12846
12847     var call = function(){
12848         var now = new Date().getTime();
12849         if(now - t >= d){
12850             clearInterval(id);
12851             id = null;
12852             fn.apply(scope, args || []);
12853         }
12854     };
12855     /**
12856      * Cancels any pending timeout and queues a new one
12857      * @param {Number} delay The milliseconds to delay
12858      * @param {Function} newFn (optional) Overrides function passed to constructor
12859      * @param {Object} newScope (optional) Overrides scope passed to constructor
12860      * @param {Array} newArgs (optional) Overrides args passed to constructor
12861      */
12862     this.delay = function(delay, newFn, newScope, newArgs){
12863         if(id && delay != d){
12864             this.cancel();
12865         }
12866         d = delay;
12867         t = new Date().getTime();
12868         fn = newFn || fn;
12869         scope = newScope || scope;
12870         args = newArgs || args;
12871         if(!id){
12872             id = setInterval(call, d);
12873         }
12874     };
12875
12876     /**
12877      * Cancel the last queued timeout
12878      */
12879     this.cancel = function(){
12880         if(id){
12881             clearInterval(id);
12882             id = null;
12883         }
12884     };
12885 };/*
12886  * Based on:
12887  * Ext JS Library 1.1.1
12888  * Copyright(c) 2006-2007, Ext JS, LLC.
12889  *
12890  * Originally Released Under LGPL - original licence link has changed is not relivant.
12891  *
12892  * Fork - LGPL
12893  * <script type="text/javascript">
12894  */
12895  
12896  
12897 Roo.util.TaskRunner = function(interval){
12898     interval = interval || 10;
12899     var tasks = [], removeQueue = [];
12900     var id = 0;
12901     var running = false;
12902
12903     var stopThread = function(){
12904         running = false;
12905         clearInterval(id);
12906         id = 0;
12907     };
12908
12909     var startThread = function(){
12910         if(!running){
12911             running = true;
12912             id = setInterval(runTasks, interval);
12913         }
12914     };
12915
12916     var removeTask = function(task){
12917         removeQueue.push(task);
12918         if(task.onStop){
12919             task.onStop();
12920         }
12921     };
12922
12923     var runTasks = function(){
12924         if(removeQueue.length > 0){
12925             for(var i = 0, len = removeQueue.length; i < len; i++){
12926                 tasks.remove(removeQueue[i]);
12927             }
12928             removeQueue = [];
12929             if(tasks.length < 1){
12930                 stopThread();
12931                 return;
12932             }
12933         }
12934         var now = new Date().getTime();
12935         for(var i = 0, len = tasks.length; i < len; ++i){
12936             var t = tasks[i];
12937             var itime = now - t.taskRunTime;
12938             if(t.interval <= itime){
12939                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12940                 t.taskRunTime = now;
12941                 if(rt === false || t.taskRunCount === t.repeat){
12942                     removeTask(t);
12943                     return;
12944                 }
12945             }
12946             if(t.duration && t.duration <= (now - t.taskStartTime)){
12947                 removeTask(t);
12948             }
12949         }
12950     };
12951
12952     /**
12953      * Queues a new task.
12954      * @param {Object} task
12955      */
12956     this.start = function(task){
12957         tasks.push(task);
12958         task.taskStartTime = new Date().getTime();
12959         task.taskRunTime = 0;
12960         task.taskRunCount = 0;
12961         startThread();
12962         return task;
12963     };
12964
12965     this.stop = function(task){
12966         removeTask(task);
12967         return task;
12968     };
12969
12970     this.stopAll = function(){
12971         stopThread();
12972         for(var i = 0, len = tasks.length; i < len; i++){
12973             if(tasks[i].onStop){
12974                 tasks[i].onStop();
12975             }
12976         }
12977         tasks = [];
12978         removeQueue = [];
12979     };
12980 };
12981
12982 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993  
12994 /**
12995  * @class Roo.util.MixedCollection
12996  * @extends Roo.util.Observable
12997  * A Collection class that maintains both numeric indexes and keys and exposes events.
12998  * @constructor
12999  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13000  * collection (defaults to false)
13001  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13002  * and return the key value for that item.  This is used when available to look up the key on items that
13003  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13004  * equivalent to providing an implementation for the {@link #getKey} method.
13005  */
13006 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13007     this.items = [];
13008     this.map = {};
13009     this.keys = [];
13010     this.length = 0;
13011     this.addEvents({
13012         /**
13013          * @event clear
13014          * Fires when the collection is cleared.
13015          */
13016         "clear" : true,
13017         /**
13018          * @event add
13019          * Fires when an item is added to the collection.
13020          * @param {Number} index The index at which the item was added.
13021          * @param {Object} o The item added.
13022          * @param {String} key The key associated with the added item.
13023          */
13024         "add" : true,
13025         /**
13026          * @event replace
13027          * Fires when an item is replaced in the collection.
13028          * @param {String} key he key associated with the new added.
13029          * @param {Object} old The item being replaced.
13030          * @param {Object} new The new item.
13031          */
13032         "replace" : true,
13033         /**
13034          * @event remove
13035          * Fires when an item is removed from the collection.
13036          * @param {Object} o The item being removed.
13037          * @param {String} key (optional) The key associated with the removed item.
13038          */
13039         "remove" : true,
13040         "sort" : true
13041     });
13042     this.allowFunctions = allowFunctions === true;
13043     if(keyFn){
13044         this.getKey = keyFn;
13045     }
13046     Roo.util.MixedCollection.superclass.constructor.call(this);
13047 };
13048
13049 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13050     allowFunctions : false,
13051     
13052 /**
13053  * Adds an item to the collection.
13054  * @param {String} key The key to associate with the item
13055  * @param {Object} o The item to add.
13056  * @return {Object} The item added.
13057  */
13058     add : function(key, o){
13059         if(arguments.length == 1){
13060             o = arguments[0];
13061             key = this.getKey(o);
13062         }
13063         if(typeof key == "undefined" || key === null){
13064             this.length++;
13065             this.items.push(o);
13066             this.keys.push(null);
13067         }else{
13068             var old = this.map[key];
13069             if(old){
13070                 return this.replace(key, o);
13071             }
13072             this.length++;
13073             this.items.push(o);
13074             this.map[key] = o;
13075             this.keys.push(key);
13076         }
13077         this.fireEvent("add", this.length-1, o, key);
13078         return o;
13079     },
13080        
13081 /**
13082   * MixedCollection has a generic way to fetch keys if you implement getKey.
13083 <pre><code>
13084 // normal way
13085 var mc = new Roo.util.MixedCollection();
13086 mc.add(someEl.dom.id, someEl);
13087 mc.add(otherEl.dom.id, otherEl);
13088 //and so on
13089
13090 // using getKey
13091 var mc = new Roo.util.MixedCollection();
13092 mc.getKey = function(el){
13093    return el.dom.id;
13094 };
13095 mc.add(someEl);
13096 mc.add(otherEl);
13097
13098 // or via the constructor
13099 var mc = new Roo.util.MixedCollection(false, function(el){
13100    return el.dom.id;
13101 });
13102 mc.add(someEl);
13103 mc.add(otherEl);
13104 </code></pre>
13105  * @param o {Object} The item for which to find the key.
13106  * @return {Object} The key for the passed item.
13107  */
13108     getKey : function(o){
13109          return o.id; 
13110     },
13111    
13112 /**
13113  * Replaces an item in the collection.
13114  * @param {String} key The key associated with the item to replace, or the item to replace.
13115  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13116  * @return {Object}  The new item.
13117  */
13118     replace : function(key, o){
13119         if(arguments.length == 1){
13120             o = arguments[0];
13121             key = this.getKey(o);
13122         }
13123         var old = this.item(key);
13124         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13125              return this.add(key, o);
13126         }
13127         var index = this.indexOfKey(key);
13128         this.items[index] = o;
13129         this.map[key] = o;
13130         this.fireEvent("replace", key, old, o);
13131         return o;
13132     },
13133    
13134 /**
13135  * Adds all elements of an Array or an Object to the collection.
13136  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13137  * an Array of values, each of which are added to the collection.
13138  */
13139     addAll : function(objs){
13140         if(arguments.length > 1 || objs instanceof Array){
13141             var args = arguments.length > 1 ? arguments : objs;
13142             for(var i = 0, len = args.length; i < len; i++){
13143                 this.add(args[i]);
13144             }
13145         }else{
13146             for(var key in objs){
13147                 if(this.allowFunctions || typeof objs[key] != "function"){
13148                     this.add(key, objs[key]);
13149                 }
13150             }
13151         }
13152     },
13153    
13154 /**
13155  * Executes the specified function once for every item in the collection, passing each
13156  * item as the first and only parameter. returning false from the function will stop the iteration.
13157  * @param {Function} fn The function to execute for each item.
13158  * @param {Object} scope (optional) The scope in which to execute the function.
13159  */
13160     each : function(fn, scope){
13161         var items = [].concat(this.items); // each safe for removal
13162         for(var i = 0, len = items.length; i < len; i++){
13163             if(fn.call(scope || items[i], items[i], i, len) === false){
13164                 break;
13165             }
13166         }
13167     },
13168    
13169 /**
13170  * Executes the specified function once for every key in the collection, passing each
13171  * key, and its associated item as the first two parameters.
13172  * @param {Function} fn The function to execute for each item.
13173  * @param {Object} scope (optional) The scope in which to execute the function.
13174  */
13175     eachKey : function(fn, scope){
13176         for(var i = 0, len = this.keys.length; i < len; i++){
13177             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13178         }
13179     },
13180    
13181 /**
13182  * Returns the first item in the collection which elicits a true return value from the
13183  * passed selection function.
13184  * @param {Function} fn The selection function to execute for each item.
13185  * @param {Object} scope (optional) The scope in which to execute the function.
13186  * @return {Object} The first item in the collection which returned true from the selection function.
13187  */
13188     find : function(fn, scope){
13189         for(var i = 0, len = this.items.length; i < len; i++){
13190             if(fn.call(scope || window, this.items[i], this.keys[i])){
13191                 return this.items[i];
13192             }
13193         }
13194         return null;
13195     },
13196    
13197 /**
13198  * Inserts an item at the specified index in the collection.
13199  * @param {Number} index The index to insert the item at.
13200  * @param {String} key The key to associate with the new item, or the item itself.
13201  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13202  * @return {Object} The item inserted.
13203  */
13204     insert : function(index, key, o){
13205         if(arguments.length == 2){
13206             o = arguments[1];
13207             key = this.getKey(o);
13208         }
13209         if(index >= this.length){
13210             return this.add(key, o);
13211         }
13212         this.length++;
13213         this.items.splice(index, 0, o);
13214         if(typeof key != "undefined" && key != null){
13215             this.map[key] = o;
13216         }
13217         this.keys.splice(index, 0, key);
13218         this.fireEvent("add", index, o, key);
13219         return o;
13220     },
13221    
13222 /**
13223  * Removed an item from the collection.
13224  * @param {Object} o The item to remove.
13225  * @return {Object} The item removed.
13226  */
13227     remove : function(o){
13228         return this.removeAt(this.indexOf(o));
13229     },
13230    
13231 /**
13232  * Remove an item from a specified index in the collection.
13233  * @param {Number} index The index within the collection of the item to remove.
13234  */
13235     removeAt : function(index){
13236         if(index < this.length && index >= 0){
13237             this.length--;
13238             var o = this.items[index];
13239             this.items.splice(index, 1);
13240             var key = this.keys[index];
13241             if(typeof key != "undefined"){
13242                 delete this.map[key];
13243             }
13244             this.keys.splice(index, 1);
13245             this.fireEvent("remove", o, key);
13246         }
13247     },
13248    
13249 /**
13250  * Removed an item associated with the passed key fom the collection.
13251  * @param {String} key The key of the item to remove.
13252  */
13253     removeKey : function(key){
13254         return this.removeAt(this.indexOfKey(key));
13255     },
13256    
13257 /**
13258  * Returns the number of items in the collection.
13259  * @return {Number} the number of items in the collection.
13260  */
13261     getCount : function(){
13262         return this.length; 
13263     },
13264    
13265 /**
13266  * Returns index within the collection of the passed Object.
13267  * @param {Object} o The item to find the index of.
13268  * @return {Number} index of the item.
13269  */
13270     indexOf : function(o){
13271         if(!this.items.indexOf){
13272             for(var i = 0, len = this.items.length; i < len; i++){
13273                 if(this.items[i] == o) {
13274                     return i;
13275                 }
13276             }
13277             return -1;
13278         }else{
13279             return this.items.indexOf(o);
13280         }
13281     },
13282    
13283 /**
13284  * Returns index within the collection of the passed key.
13285  * @param {String} key The key to find the index of.
13286  * @return {Number} index of the key.
13287  */
13288     indexOfKey : function(key){
13289         if(!this.keys.indexOf){
13290             for(var i = 0, len = this.keys.length; i < len; i++){
13291                 if(this.keys[i] == key) {
13292                     return i;
13293                 }
13294             }
13295             return -1;
13296         }else{
13297             return this.keys.indexOf(key);
13298         }
13299     },
13300    
13301 /**
13302  * Returns the item associated with the passed key OR index. Key has priority over index.
13303  * @param {String/Number} key The key or index of the item.
13304  * @return {Object} The item associated with the passed key.
13305  */
13306     item : function(key){
13307         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13308         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13309     },
13310     
13311 /**
13312  * Returns the item at the specified index.
13313  * @param {Number} index The index of the item.
13314  * @return {Object}
13315  */
13316     itemAt : function(index){
13317         return this.items[index];
13318     },
13319     
13320 /**
13321  * Returns the item associated with the passed key.
13322  * @param {String/Number} key The key of the item.
13323  * @return {Object} The item associated with the passed key.
13324  */
13325     key : function(key){
13326         return this.map[key];
13327     },
13328    
13329 /**
13330  * Returns true if the collection contains the passed Object as an item.
13331  * @param {Object} o  The Object to look for in the collection.
13332  * @return {Boolean} True if the collection contains the Object as an item.
13333  */
13334     contains : function(o){
13335         return this.indexOf(o) != -1;
13336     },
13337    
13338 /**
13339  * Returns true if the collection contains the passed Object as a key.
13340  * @param {String} key The key to look for in the collection.
13341  * @return {Boolean} True if the collection contains the Object as a key.
13342  */
13343     containsKey : function(key){
13344         return typeof this.map[key] != "undefined";
13345     },
13346    
13347 /**
13348  * Removes all items from the collection.
13349  */
13350     clear : function(){
13351         this.length = 0;
13352         this.items = [];
13353         this.keys = [];
13354         this.map = {};
13355         this.fireEvent("clear");
13356     },
13357    
13358 /**
13359  * Returns the first item in the collection.
13360  * @return {Object} the first item in the collection..
13361  */
13362     first : function(){
13363         return this.items[0]; 
13364     },
13365    
13366 /**
13367  * Returns the last item in the collection.
13368  * @return {Object} the last item in the collection..
13369  */
13370     last : function(){
13371         return this.items[this.length-1];   
13372     },
13373     
13374     _sort : function(property, dir, fn){
13375         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13376         fn = fn || function(a, b){
13377             return a-b;
13378         };
13379         var c = [], k = this.keys, items = this.items;
13380         for(var i = 0, len = items.length; i < len; i++){
13381             c[c.length] = {key: k[i], value: items[i], index: i};
13382         }
13383         c.sort(function(a, b){
13384             var v = fn(a[property], b[property]) * dsc;
13385             if(v == 0){
13386                 v = (a.index < b.index ? -1 : 1);
13387             }
13388             return v;
13389         });
13390         for(var i = 0, len = c.length; i < len; i++){
13391             items[i] = c[i].value;
13392             k[i] = c[i].key;
13393         }
13394         this.fireEvent("sort", this);
13395     },
13396     
13397     /**
13398      * Sorts this collection with the passed comparison function
13399      * @param {String} direction (optional) "ASC" or "DESC"
13400      * @param {Function} fn (optional) comparison function
13401      */
13402     sort : function(dir, fn){
13403         this._sort("value", dir, fn);
13404     },
13405     
13406     /**
13407      * Sorts this collection by keys
13408      * @param {String} direction (optional) "ASC" or "DESC"
13409      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13410      */
13411     keySort : function(dir, fn){
13412         this._sort("key", dir, fn || function(a, b){
13413             return String(a).toUpperCase()-String(b).toUpperCase();
13414         });
13415     },
13416     
13417     /**
13418      * Returns a range of items in this collection
13419      * @param {Number} startIndex (optional) defaults to 0
13420      * @param {Number} endIndex (optional) default to the last item
13421      * @return {Array} An array of items
13422      */
13423     getRange : function(start, end){
13424         var items = this.items;
13425         if(items.length < 1){
13426             return [];
13427         }
13428         start = start || 0;
13429         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13430         var r = [];
13431         if(start <= end){
13432             for(var i = start; i <= end; i++) {
13433                     r[r.length] = items[i];
13434             }
13435         }else{
13436             for(var i = start; i >= end; i--) {
13437                     r[r.length] = items[i];
13438             }
13439         }
13440         return r;
13441     },
13442         
13443     /**
13444      * Filter the <i>objects</i> in this collection by a specific property. 
13445      * Returns a new collection that has been filtered.
13446      * @param {String} property A property on your objects
13447      * @param {String/RegExp} value Either string that the property values 
13448      * should start with or a RegExp to test against the property
13449      * @return {MixedCollection} The new filtered collection
13450      */
13451     filter : function(property, value){
13452         if(!value.exec){ // not a regex
13453             value = String(value);
13454             if(value.length == 0){
13455                 return this.clone();
13456             }
13457             value = new RegExp("^" + Roo.escapeRe(value), "i");
13458         }
13459         return this.filterBy(function(o){
13460             return o && value.test(o[property]);
13461         });
13462         },
13463     
13464     /**
13465      * Filter by a function. * Returns a new collection that has been filtered.
13466      * The passed function will be called with each 
13467      * object in the collection. If the function returns true, the value is included 
13468      * otherwise it is filtered.
13469      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13470      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13471      * @return {MixedCollection} The new filtered collection
13472      */
13473     filterBy : function(fn, scope){
13474         var r = new Roo.util.MixedCollection();
13475         r.getKey = this.getKey;
13476         var k = this.keys, it = this.items;
13477         for(var i = 0, len = it.length; i < len; i++){
13478             if(fn.call(scope||this, it[i], k[i])){
13479                                 r.add(k[i], it[i]);
13480                         }
13481         }
13482         return r;
13483     },
13484     
13485     /**
13486      * Creates a duplicate of this collection
13487      * @return {MixedCollection}
13488      */
13489     clone : function(){
13490         var r = new Roo.util.MixedCollection();
13491         var k = this.keys, it = this.items;
13492         for(var i = 0, len = it.length; i < len; i++){
13493             r.add(k[i], it[i]);
13494         }
13495         r.getKey = this.getKey;
13496         return r;
13497     }
13498 });
13499 /**
13500  * Returns the item associated with the passed key or index.
13501  * @method
13502  * @param {String/Number} key The key or index of the item.
13503  * @return {Object} The item associated with the passed key.
13504  */
13505 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13506  * Based on:
13507  * Ext JS Library 1.1.1
13508  * Copyright(c) 2006-2007, Ext JS, LLC.
13509  *
13510  * Originally Released Under LGPL - original licence link has changed is not relivant.
13511  *
13512  * Fork - LGPL
13513  * <script type="text/javascript">
13514  */
13515 /**
13516  * @class Roo.util.JSON
13517  * Modified version of Douglas Crockford"s json.js that doesn"t
13518  * mess with the Object prototype 
13519  * http://www.json.org/js.html
13520  * @singleton
13521  */
13522 Roo.util.JSON = new (function(){
13523     var useHasOwn = {}.hasOwnProperty ? true : false;
13524     
13525     // crashes Safari in some instances
13526     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13527     
13528     var pad = function(n) {
13529         return n < 10 ? "0" + n : n;
13530     };
13531     
13532     var m = {
13533         "\b": '\\b',
13534         "\t": '\\t',
13535         "\n": '\\n',
13536         "\f": '\\f',
13537         "\r": '\\r',
13538         '"' : '\\"',
13539         "\\": '\\\\'
13540     };
13541
13542     var encodeString = function(s){
13543         if (/["\\\x00-\x1f]/.test(s)) {
13544             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13545                 var c = m[b];
13546                 if(c){
13547                     return c;
13548                 }
13549                 c = b.charCodeAt();
13550                 return "\\u00" +
13551                     Math.floor(c / 16).toString(16) +
13552                     (c % 16).toString(16);
13553             }) + '"';
13554         }
13555         return '"' + s + '"';
13556     };
13557     
13558     var encodeArray = function(o){
13559         var a = ["["], b, i, l = o.length, v;
13560             for (i = 0; i < l; i += 1) {
13561                 v = o[i];
13562                 switch (typeof v) {
13563                     case "undefined":
13564                     case "function":
13565                     case "unknown":
13566                         break;
13567                     default:
13568                         if (b) {
13569                             a.push(',');
13570                         }
13571                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13572                         b = true;
13573                 }
13574             }
13575             a.push("]");
13576             return a.join("");
13577     };
13578     
13579     var encodeDate = function(o){
13580         return '"' + o.getFullYear() + "-" +
13581                 pad(o.getMonth() + 1) + "-" +
13582                 pad(o.getDate()) + "T" +
13583                 pad(o.getHours()) + ":" +
13584                 pad(o.getMinutes()) + ":" +
13585                 pad(o.getSeconds()) + '"';
13586     };
13587     
13588     /**
13589      * Encodes an Object, Array or other value
13590      * @param {Mixed} o The variable to encode
13591      * @return {String} The JSON string
13592      */
13593     this.encode = function(o)
13594     {
13595         // should this be extended to fully wrap stringify..
13596         
13597         if(typeof o == "undefined" || o === null){
13598             return "null";
13599         }else if(o instanceof Array){
13600             return encodeArray(o);
13601         }else if(o instanceof Date){
13602             return encodeDate(o);
13603         }else if(typeof o == "string"){
13604             return encodeString(o);
13605         }else if(typeof o == "number"){
13606             return isFinite(o) ? String(o) : "null";
13607         }else if(typeof o == "boolean"){
13608             return String(o);
13609         }else {
13610             var a = ["{"], b, i, v;
13611             for (i in o) {
13612                 if(!useHasOwn || o.hasOwnProperty(i)) {
13613                     v = o[i];
13614                     switch (typeof v) {
13615                     case "undefined":
13616                     case "function":
13617                     case "unknown":
13618                         break;
13619                     default:
13620                         if(b){
13621                             a.push(',');
13622                         }
13623                         a.push(this.encode(i), ":",
13624                                 v === null ? "null" : this.encode(v));
13625                         b = true;
13626                     }
13627                 }
13628             }
13629             a.push("}");
13630             return a.join("");
13631         }
13632     };
13633     
13634     /**
13635      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13636      * @param {String} json The JSON string
13637      * @return {Object} The resulting object
13638      */
13639     this.decode = function(json){
13640         
13641         return  /** eval:var:json */ eval("(" + json + ')');
13642     };
13643 })();
13644 /** 
13645  * Shorthand for {@link Roo.util.JSON#encode}
13646  * @member Roo encode 
13647  * @method */
13648 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13649 /** 
13650  * Shorthand for {@link Roo.util.JSON#decode}
13651  * @member Roo decode 
13652  * @method */
13653 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13654 /*
13655  * Based on:
13656  * Ext JS Library 1.1.1
13657  * Copyright(c) 2006-2007, Ext JS, LLC.
13658  *
13659  * Originally Released Under LGPL - original licence link has changed is not relivant.
13660  *
13661  * Fork - LGPL
13662  * <script type="text/javascript">
13663  */
13664  
13665 /**
13666  * @class Roo.util.Format
13667  * Reusable data formatting functions
13668  * @singleton
13669  */
13670 Roo.util.Format = function(){
13671     var trimRe = /^\s+|\s+$/g;
13672     return {
13673         /**
13674          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13675          * @param {String} value The string to truncate
13676          * @param {Number} length The maximum length to allow before truncating
13677          * @return {String} The converted text
13678          */
13679         ellipsis : function(value, len){
13680             if(value && value.length > len){
13681                 return value.substr(0, len-3)+"...";
13682             }
13683             return value;
13684         },
13685
13686         /**
13687          * Checks a reference and converts it to empty string if it is undefined
13688          * @param {Mixed} value Reference to check
13689          * @return {Mixed} Empty string if converted, otherwise the original value
13690          */
13691         undef : function(value){
13692             return typeof value != "undefined" ? value : "";
13693         },
13694
13695         /**
13696          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13697          * @param {String} value The string to encode
13698          * @return {String} The encoded text
13699          */
13700         htmlEncode : function(value){
13701             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13702         },
13703
13704         /**
13705          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13706          * @param {String} value The string to decode
13707          * @return {String} The decoded text
13708          */
13709         htmlDecode : function(value){
13710             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13711         },
13712
13713         /**
13714          * Trims any whitespace from either side of a string
13715          * @param {String} value The text to trim
13716          * @return {String} The trimmed text
13717          */
13718         trim : function(value){
13719             return String(value).replace(trimRe, "");
13720         },
13721
13722         /**
13723          * Returns a substring from within an original string
13724          * @param {String} value The original text
13725          * @param {Number} start The start index of the substring
13726          * @param {Number} length The length of the substring
13727          * @return {String} The substring
13728          */
13729         substr : function(value, start, length){
13730             return String(value).substr(start, length);
13731         },
13732
13733         /**
13734          * Converts a string to all lower case letters
13735          * @param {String} value The text to convert
13736          * @return {String} The converted text
13737          */
13738         lowercase : function(value){
13739             return String(value).toLowerCase();
13740         },
13741
13742         /**
13743          * Converts a string to all upper case letters
13744          * @param {String} value The text to convert
13745          * @return {String} The converted text
13746          */
13747         uppercase : function(value){
13748             return String(value).toUpperCase();
13749         },
13750
13751         /**
13752          * Converts the first character only of a string to upper case
13753          * @param {String} value The text to convert
13754          * @return {String} The converted text
13755          */
13756         capitalize : function(value){
13757             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13758         },
13759
13760         // private
13761         call : function(value, fn){
13762             if(arguments.length > 2){
13763                 var args = Array.prototype.slice.call(arguments, 2);
13764                 args.unshift(value);
13765                  
13766                 return /** eval:var:value */  eval(fn).apply(window, args);
13767             }else{
13768                 /** eval:var:value */
13769                 return /** eval:var:value */ eval(fn).call(window, value);
13770             }
13771         },
13772
13773        
13774         /**
13775          * safer version of Math.toFixed..??/
13776          * @param {Number/String} value The numeric value to format
13777          * @param {Number/String} value Decimal places 
13778          * @return {String} The formatted currency string
13779          */
13780         toFixed : function(v, n)
13781         {
13782             // why not use to fixed - precision is buggered???
13783             if (!n) {
13784                 return Math.round(v-0);
13785             }
13786             var fact = Math.pow(10,n+1);
13787             v = (Math.round((v-0)*fact))/fact;
13788             var z = (''+fact).substring(2);
13789             if (v == Math.floor(v)) {
13790                 return Math.floor(v) + '.' + z;
13791             }
13792             
13793             // now just padd decimals..
13794             var ps = String(v).split('.');
13795             var fd = (ps[1] + z);
13796             var r = fd.substring(0,n); 
13797             var rm = fd.substring(n); 
13798             if (rm < 5) {
13799                 return ps[0] + '.' + r;
13800             }
13801             r*=1; // turn it into a number;
13802             r++;
13803             if (String(r).length != n) {
13804                 ps[0]*=1;
13805                 ps[0]++;
13806                 r = String(r).substring(1); // chop the end off.
13807             }
13808             
13809             return ps[0] + '.' + r;
13810              
13811         },
13812         
13813         /**
13814          * Format a number as US currency
13815          * @param {Number/String} value The numeric value to format
13816          * @return {String} The formatted currency string
13817          */
13818         usMoney : function(v){
13819             return '$' + Roo.util.Format.number(v);
13820         },
13821         
13822         /**
13823          * Format a number
13824          * eventually this should probably emulate php's number_format
13825          * @param {Number/String} value The numeric value to format
13826          * @param {Number} decimals number of decimal places
13827          * @param {String} delimiter for thousands (default comma)
13828          * @return {String} The formatted currency string
13829          */
13830         number : function(v, decimals, thousandsDelimiter)
13831         {
13832             // multiply and round.
13833             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13834             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13835             
13836             var mul = Math.pow(10, decimals);
13837             var zero = String(mul).substring(1);
13838             v = (Math.round((v-0)*mul))/mul;
13839             
13840             // if it's '0' number.. then
13841             
13842             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13843             v = String(v);
13844             var ps = v.split('.');
13845             var whole = ps[0];
13846             
13847             var r = /(\d+)(\d{3})/;
13848             // add comma's
13849             
13850             if(thousandsDelimiter.length != 0) {
13851                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13852             } 
13853             
13854             var sub = ps[1] ?
13855                     // has decimals..
13856                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13857                     // does not have decimals
13858                     (decimals ? ('.' + zero) : '');
13859             
13860             
13861             return whole + sub ;
13862         },
13863         
13864         /**
13865          * Parse a value into a formatted date using the specified format pattern.
13866          * @param {Mixed} value The value to format
13867          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13868          * @return {String} The formatted date string
13869          */
13870         date : function(v, format){
13871             if(!v){
13872                 return "";
13873             }
13874             if(!(v instanceof Date)){
13875                 v = new Date(Date.parse(v));
13876             }
13877             return v.dateFormat(format || Roo.util.Format.defaults.date);
13878         },
13879
13880         /**
13881          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13882          * @param {String} format Any valid date format string
13883          * @return {Function} The date formatting function
13884          */
13885         dateRenderer : function(format){
13886             return function(v){
13887                 return Roo.util.Format.date(v, format);  
13888             };
13889         },
13890
13891         // private
13892         stripTagsRE : /<\/?[^>]+>/gi,
13893         
13894         /**
13895          * Strips all HTML tags
13896          * @param {Mixed} value The text from which to strip tags
13897          * @return {String} The stripped text
13898          */
13899         stripTags : function(v){
13900             return !v ? v : String(v).replace(this.stripTagsRE, "");
13901         }
13902     };
13903 }();
13904 Roo.util.Format.defaults = {
13905     date : 'd/M/Y'
13906 };/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917
13918  
13919
13920 /**
13921  * @class Roo.MasterTemplate
13922  * @extends Roo.Template
13923  * Provides a template that can have child templates. The syntax is:
13924 <pre><code>
13925 var t = new Roo.MasterTemplate(
13926         '&lt;select name="{name}"&gt;',
13927                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13928         '&lt;/select&gt;'
13929 );
13930 t.add('options', {value: 'foo', text: 'bar'});
13931 // or you can add multiple child elements in one shot
13932 t.addAll('options', [
13933     {value: 'foo', text: 'bar'},
13934     {value: 'foo2', text: 'bar2'},
13935     {value: 'foo3', text: 'bar3'}
13936 ]);
13937 // then append, applying the master template values
13938 t.append('my-form', {name: 'my-select'});
13939 </code></pre>
13940 * A name attribute for the child template is not required if you have only one child
13941 * template or you want to refer to them by index.
13942  */
13943 Roo.MasterTemplate = function(){
13944     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13945     this.originalHtml = this.html;
13946     var st = {};
13947     var m, re = this.subTemplateRe;
13948     re.lastIndex = 0;
13949     var subIndex = 0;
13950     while(m = re.exec(this.html)){
13951         var name = m[1], content = m[2];
13952         st[subIndex] = {
13953             name: name,
13954             index: subIndex,
13955             buffer: [],
13956             tpl : new Roo.Template(content)
13957         };
13958         if(name){
13959             st[name] = st[subIndex];
13960         }
13961         st[subIndex].tpl.compile();
13962         st[subIndex].tpl.call = this.call.createDelegate(this);
13963         subIndex++;
13964     }
13965     this.subCount = subIndex;
13966     this.subs = st;
13967 };
13968 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13969     /**
13970     * The regular expression used to match sub templates
13971     * @type RegExp
13972     * @property
13973     */
13974     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13975
13976     /**
13977      * Applies the passed values to a child template.
13978      * @param {String/Number} name (optional) The name or index of the child template
13979      * @param {Array/Object} values The values to be applied to the template
13980      * @return {MasterTemplate} this
13981      */
13982      add : function(name, values){
13983         if(arguments.length == 1){
13984             values = arguments[0];
13985             name = 0;
13986         }
13987         var s = this.subs[name];
13988         s.buffer[s.buffer.length] = s.tpl.apply(values);
13989         return this;
13990     },
13991
13992     /**
13993      * Applies all the passed values to a child template.
13994      * @param {String/Number} name (optional) The name or index of the child template
13995      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13996      * @param {Boolean} reset (optional) True to reset the template first
13997      * @return {MasterTemplate} this
13998      */
13999     fill : function(name, values, reset){
14000         var a = arguments;
14001         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14002             values = a[0];
14003             name = 0;
14004             reset = a[1];
14005         }
14006         if(reset){
14007             this.reset();
14008         }
14009         for(var i = 0, len = values.length; i < len; i++){
14010             this.add(name, values[i]);
14011         }
14012         return this;
14013     },
14014
14015     /**
14016      * Resets the template for reuse
14017      * @return {MasterTemplate} this
14018      */
14019      reset : function(){
14020         var s = this.subs;
14021         for(var i = 0; i < this.subCount; i++){
14022             s[i].buffer = [];
14023         }
14024         return this;
14025     },
14026
14027     applyTemplate : function(values){
14028         var s = this.subs;
14029         var replaceIndex = -1;
14030         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14031             return s[++replaceIndex].buffer.join("");
14032         });
14033         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14034     },
14035
14036     apply : function(){
14037         return this.applyTemplate.apply(this, arguments);
14038     },
14039
14040     compile : function(){return this;}
14041 });
14042
14043 /**
14044  * Alias for fill().
14045  * @method
14046  */
14047 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14048  /**
14049  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14050  * var tpl = Roo.MasterTemplate.from('element-id');
14051  * @param {String/HTMLElement} el
14052  * @param {Object} config
14053  * @static
14054  */
14055 Roo.MasterTemplate.from = function(el, config){
14056     el = Roo.getDom(el);
14057     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.CSS
14072  * Utility class for manipulating CSS rules
14073  * @singleton
14074  */
14075 Roo.util.CSS = function(){
14076         var rules = null;
14077         var doc = document;
14078
14079     var camelRe = /(-[a-z])/gi;
14080     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14081
14082    return {
14083    /**
14084     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14085     * tag and appended to the HEAD of the document.
14086     * @param {String|Object} cssText The text containing the css rules
14087     * @param {String} id An id to add to the stylesheet for later removal
14088     * @return {StyleSheet}
14089     */
14090     createStyleSheet : function(cssText, id){
14091         var ss;
14092         var head = doc.getElementsByTagName("head")[0];
14093         var nrules = doc.createElement("style");
14094         nrules.setAttribute("type", "text/css");
14095         if(id){
14096             nrules.setAttribute("id", id);
14097         }
14098         if (typeof(cssText) != 'string') {
14099             // support object maps..
14100             // not sure if this a good idea.. 
14101             // perhaps it should be merged with the general css handling
14102             // and handle js style props.
14103             var cssTextNew = [];
14104             for(var n in cssText) {
14105                 var citems = [];
14106                 for(var k in cssText[n]) {
14107                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14108                 }
14109                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14110                 
14111             }
14112             cssText = cssTextNew.join("\n");
14113             
14114         }
14115        
14116        
14117        if(Roo.isIE){
14118            head.appendChild(nrules);
14119            ss = nrules.styleSheet;
14120            ss.cssText = cssText;
14121        }else{
14122            try{
14123                 nrules.appendChild(doc.createTextNode(cssText));
14124            }catch(e){
14125                nrules.cssText = cssText; 
14126            }
14127            head.appendChild(nrules);
14128            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14129        }
14130        this.cacheStyleSheet(ss);
14131        return ss;
14132    },
14133
14134    /**
14135     * Removes a style or link tag by id
14136     * @param {String} id The id of the tag
14137     */
14138    removeStyleSheet : function(id){
14139        var existing = doc.getElementById(id);
14140        if(existing){
14141            existing.parentNode.removeChild(existing);
14142        }
14143    },
14144
14145    /**
14146     * Dynamically swaps an existing stylesheet reference for a new one
14147     * @param {String} id The id of an existing link tag to remove
14148     * @param {String} url The href of the new stylesheet to include
14149     */
14150    swapStyleSheet : function(id, url){
14151        this.removeStyleSheet(id);
14152        var ss = doc.createElement("link");
14153        ss.setAttribute("rel", "stylesheet");
14154        ss.setAttribute("type", "text/css");
14155        ss.setAttribute("id", id);
14156        ss.setAttribute("href", url);
14157        doc.getElementsByTagName("head")[0].appendChild(ss);
14158    },
14159    
14160    /**
14161     * Refresh the rule cache if you have dynamically added stylesheets
14162     * @return {Object} An object (hash) of rules indexed by selector
14163     */
14164    refreshCache : function(){
14165        return this.getRules(true);
14166    },
14167
14168    // private
14169    cacheStyleSheet : function(stylesheet){
14170        if(!rules){
14171            rules = {};
14172        }
14173        try{// try catch for cross domain access issue
14174            var ssRules = stylesheet.cssRules || stylesheet.rules;
14175            for(var j = ssRules.length-1; j >= 0; --j){
14176                rules[ssRules[j].selectorText] = ssRules[j];
14177            }
14178        }catch(e){}
14179    },
14180    
14181    /**
14182     * Gets all css rules for the document
14183     * @param {Boolean} refreshCache true to refresh the internal cache
14184     * @return {Object} An object (hash) of rules indexed by selector
14185     */
14186    getRules : function(refreshCache){
14187                 if(rules == null || refreshCache){
14188                         rules = {};
14189                         var ds = doc.styleSheets;
14190                         for(var i =0, len = ds.length; i < len; i++){
14191                             try{
14192                         this.cacheStyleSheet(ds[i]);
14193                     }catch(e){} 
14194                 }
14195                 }
14196                 return rules;
14197         },
14198         
14199         /**
14200     * Gets an an individual CSS rule by selector(s)
14201     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14202     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14203     * @return {CSSRule} The CSS rule or null if one is not found
14204     */
14205    getRule : function(selector, refreshCache){
14206                 var rs = this.getRules(refreshCache);
14207                 if(!(selector instanceof Array)){
14208                     return rs[selector];
14209                 }
14210                 for(var i = 0; i < selector.length; i++){
14211                         if(rs[selector[i]]){
14212                                 return rs[selector[i]];
14213                         }
14214                 }
14215                 return null;
14216         },
14217         
14218         
14219         /**
14220     * Updates a rule property
14221     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14222     * @param {String} property The css property
14223     * @param {String} value The new value for the property
14224     * @return {Boolean} true If a rule was found and updated
14225     */
14226    updateRule : function(selector, property, value){
14227                 if(!(selector instanceof Array)){
14228                         var rule = this.getRule(selector);
14229                         if(rule){
14230                                 rule.style[property.replace(camelRe, camelFn)] = value;
14231                                 return true;
14232                         }
14233                 }else{
14234                         for(var i = 0; i < selector.length; i++){
14235                                 if(this.updateRule(selector[i], property, value)){
14236                                         return true;
14237                                 }
14238                         }
14239                 }
14240                 return false;
14241         }
14242    };   
14243 }();/*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253
14254  
14255
14256 /**
14257  * @class Roo.util.ClickRepeater
14258  * @extends Roo.util.Observable
14259  * 
14260  * A wrapper class which can be applied to any element. Fires a "click" event while the
14261  * mouse is pressed. The interval between firings may be specified in the config but
14262  * defaults to 10 milliseconds.
14263  * 
14264  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14265  * 
14266  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14267  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14268  * Similar to an autorepeat key delay.
14269  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14270  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14271  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14272  *           "interval" and "delay" are ignored. "immediate" is honored.
14273  * @cfg {Boolean} preventDefault True to prevent the default click event
14274  * @cfg {Boolean} stopDefault True to stop the default click event
14275  * 
14276  * @history
14277  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14278  *     2007-02-02 jvs Renamed to ClickRepeater
14279  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14280  *
14281  *  @constructor
14282  * @param {String/HTMLElement/Element} el The element to listen on
14283  * @param {Object} config
14284  **/
14285 Roo.util.ClickRepeater = function(el, config)
14286 {
14287     this.el = Roo.get(el);
14288     this.el.unselectable();
14289
14290     Roo.apply(this, config);
14291
14292     this.addEvents({
14293     /**
14294      * @event mousedown
14295      * Fires when the mouse button is depressed.
14296      * @param {Roo.util.ClickRepeater} this
14297      */
14298         "mousedown" : true,
14299     /**
14300      * @event click
14301      * Fires on a specified interval during the time the element is pressed.
14302      * @param {Roo.util.ClickRepeater} this
14303      */
14304         "click" : true,
14305     /**
14306      * @event mouseup
14307      * Fires when the mouse key is released.
14308      * @param {Roo.util.ClickRepeater} this
14309      */
14310         "mouseup" : true
14311     });
14312
14313     this.el.on("mousedown", this.handleMouseDown, this);
14314     if(this.preventDefault || this.stopDefault){
14315         this.el.on("click", function(e){
14316             if(this.preventDefault){
14317                 e.preventDefault();
14318             }
14319             if(this.stopDefault){
14320                 e.stopEvent();
14321             }
14322         }, this);
14323     }
14324
14325     // allow inline handler
14326     if(this.handler){
14327         this.on("click", this.handler,  this.scope || this);
14328     }
14329
14330     Roo.util.ClickRepeater.superclass.constructor.call(this);
14331 };
14332
14333 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14334     interval : 20,
14335     delay: 250,
14336     preventDefault : true,
14337     stopDefault : false,
14338     timer : 0,
14339
14340     // private
14341     handleMouseDown : function(){
14342         clearTimeout(this.timer);
14343         this.el.blur();
14344         if(this.pressClass){
14345             this.el.addClass(this.pressClass);
14346         }
14347         this.mousedownTime = new Date();
14348
14349         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14350         this.el.on("mouseout", this.handleMouseOut, this);
14351
14352         this.fireEvent("mousedown", this);
14353         this.fireEvent("click", this);
14354         
14355         this.timer = this.click.defer(this.delay || this.interval, this);
14356     },
14357
14358     // private
14359     click : function(){
14360         this.fireEvent("click", this);
14361         this.timer = this.click.defer(this.getInterval(), this);
14362     },
14363
14364     // private
14365     getInterval: function(){
14366         if(!this.accelerate){
14367             return this.interval;
14368         }
14369         var pressTime = this.mousedownTime.getElapsed();
14370         if(pressTime < 500){
14371             return 400;
14372         }else if(pressTime < 1700){
14373             return 320;
14374         }else if(pressTime < 2600){
14375             return 250;
14376         }else if(pressTime < 3500){
14377             return 180;
14378         }else if(pressTime < 4400){
14379             return 140;
14380         }else if(pressTime < 5300){
14381             return 80;
14382         }else if(pressTime < 6200){
14383             return 50;
14384         }else{
14385             return 10;
14386         }
14387     },
14388
14389     // private
14390     handleMouseOut : function(){
14391         clearTimeout(this.timer);
14392         if(this.pressClass){
14393             this.el.removeClass(this.pressClass);
14394         }
14395         this.el.on("mouseover", this.handleMouseReturn, this);
14396     },
14397
14398     // private
14399     handleMouseReturn : function(){
14400         this.el.un("mouseover", this.handleMouseReturn);
14401         if(this.pressClass){
14402             this.el.addClass(this.pressClass);
14403         }
14404         this.click();
14405     },
14406
14407     // private
14408     handleMouseUp : function(){
14409         clearTimeout(this.timer);
14410         this.el.un("mouseover", this.handleMouseReturn);
14411         this.el.un("mouseout", this.handleMouseOut);
14412         Roo.get(document).un("mouseup", this.handleMouseUp);
14413         this.el.removeClass(this.pressClass);
14414         this.fireEvent("mouseup", this);
14415     }
14416 });/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426
14427  
14428 /**
14429  * @class Roo.KeyNav
14430  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14431  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14432  * way to implement custom navigation schemes for any UI component.</p>
14433  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14434  * pageUp, pageDown, del, home, end.  Usage:</p>
14435  <pre><code>
14436 var nav = new Roo.KeyNav("my-element", {
14437     "left" : function(e){
14438         this.moveLeft(e.ctrlKey);
14439     },
14440     "right" : function(e){
14441         this.moveRight(e.ctrlKey);
14442     },
14443     "enter" : function(e){
14444         this.save();
14445     },
14446     scope : this
14447 });
14448 </code></pre>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config
14452  */
14453 Roo.KeyNav = function(el, config){
14454     this.el = Roo.get(el);
14455     Roo.apply(this, config);
14456     if(!this.disabled){
14457         this.disabled = true;
14458         this.enable();
14459     }
14460 };
14461
14462 Roo.KeyNav.prototype = {
14463     /**
14464      * @cfg {Boolean} disabled
14465      * True to disable this KeyNav instance (defaults to false)
14466      */
14467     disabled : false,
14468     /**
14469      * @cfg {String} defaultEventAction
14470      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14471      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14472      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14473      */
14474     defaultEventAction: "stopEvent",
14475     /**
14476      * @cfg {Boolean} forceKeyDown
14477      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14478      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14479      * handle keydown instead of keypress.
14480      */
14481     forceKeyDown : false,
14482
14483     // private
14484     prepareEvent : function(e){
14485         var k = e.getKey();
14486         var h = this.keyToHandler[k];
14487         //if(h && this[h]){
14488         //    e.stopPropagation();
14489         //}
14490         if(Roo.isSafari && h && k >= 37 && k <= 40){
14491             e.stopEvent();
14492         }
14493     },
14494
14495     // private
14496     relay : function(e){
14497         var k = e.getKey();
14498         var h = this.keyToHandler[k];
14499         if(h && this[h]){
14500             if(this.doRelay(e, this[h], h) !== true){
14501                 e[this.defaultEventAction]();
14502             }
14503         }
14504     },
14505
14506     // private
14507     doRelay : function(e, h, hname){
14508         return h.call(this.scope || this, e);
14509     },
14510
14511     // possible handlers
14512     enter : false,
14513     left : false,
14514     right : false,
14515     up : false,
14516     down : false,
14517     tab : false,
14518     esc : false,
14519     pageUp : false,
14520     pageDown : false,
14521     del : false,
14522     home : false,
14523     end : false,
14524
14525     // quick lookup hash
14526     keyToHandler : {
14527         37 : "left",
14528         39 : "right",
14529         38 : "up",
14530         40 : "down",
14531         33 : "pageUp",
14532         34 : "pageDown",
14533         46 : "del",
14534         36 : "home",
14535         35 : "end",
14536         13 : "enter",
14537         27 : "esc",
14538         9  : "tab"
14539     },
14540
14541         /**
14542          * Enable this KeyNav
14543          */
14544         enable: function(){
14545                 if(this.disabled){
14546             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14547             // the EventObject will normalize Safari automatically
14548             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14549                 this.el.on("keydown", this.relay,  this);
14550             }else{
14551                 this.el.on("keydown", this.prepareEvent,  this);
14552                 this.el.on("keypress", this.relay,  this);
14553             }
14554                     this.disabled = false;
14555                 }
14556         },
14557
14558         /**
14559          * Disable this KeyNav
14560          */
14561         disable: function(){
14562                 if(!this.disabled){
14563                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14564                 this.el.un("keydown", this.relay);
14565             }else{
14566                 this.el.un("keydown", this.prepareEvent);
14567                 this.el.un("keypress", this.relay);
14568             }
14569                     this.disabled = true;
14570                 }
14571         }
14572 };/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583  
14584 /**
14585  * @class Roo.KeyMap
14586  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14587  * The constructor accepts the same config object as defined by {@link #addBinding}.
14588  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14589  * combination it will call the function with this signature (if the match is a multi-key
14590  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14591  * A KeyMap can also handle a string representation of keys.<br />
14592  * Usage:
14593  <pre><code>
14594 // map one key by key code
14595 var map = new Roo.KeyMap("my-element", {
14596     key: 13, // or Roo.EventObject.ENTER
14597     fn: myHandler,
14598     scope: myObject
14599 });
14600
14601 // map multiple keys to one action by string
14602 var map = new Roo.KeyMap("my-element", {
14603     key: "a\r\n\t",
14604     fn: myHandler,
14605     scope: myObject
14606 });
14607
14608 // map multiple keys to multiple actions by strings and array of codes
14609 var map = new Roo.KeyMap("my-element", [
14610     {
14611         key: [10,13],
14612         fn: function(){ alert("Return was pressed"); }
14613     }, {
14614         key: "abc",
14615         fn: function(){ alert('a, b or c was pressed'); }
14616     }, {
14617         key: "\t",
14618         ctrl:true,
14619         shift:true,
14620         fn: function(){ alert('Control + shift + tab was pressed.'); }
14621     }
14622 ]);
14623 </code></pre>
14624  * <b>Note: A KeyMap starts enabled</b>
14625  * @constructor
14626  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14627  * @param {Object} config The config (see {@link #addBinding})
14628  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14629  */
14630 Roo.KeyMap = function(el, config, eventName){
14631     this.el  = Roo.get(el);
14632     this.eventName = eventName || "keydown";
14633     this.bindings = [];
14634     if(config){
14635         this.addBinding(config);
14636     }
14637     this.enable();
14638 };
14639
14640 Roo.KeyMap.prototype = {
14641     /**
14642      * True to stop the event from bubbling and prevent the default browser action if the
14643      * key was handled by the KeyMap (defaults to false)
14644      * @type Boolean
14645      */
14646     stopEvent : false,
14647
14648     /**
14649      * Add a new binding to this KeyMap. The following config object properties are supported:
14650      * <pre>
14651 Property    Type             Description
14652 ----------  ---------------  ----------------------------------------------------------------------
14653 key         String/Array     A single keycode or an array of keycodes to handle
14654 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14655 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14656 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14657 fn          Function         The function to call when KeyMap finds the expected key combination
14658 scope       Object           The scope of the callback function
14659 </pre>
14660      *
14661      * Usage:
14662      * <pre><code>
14663 // Create a KeyMap
14664 var map = new Roo.KeyMap(document, {
14665     key: Roo.EventObject.ENTER,
14666     fn: handleKey,
14667     scope: this
14668 });
14669
14670 //Add a new binding to the existing KeyMap later
14671 map.addBinding({
14672     key: 'abc',
14673     shift: true,
14674     fn: handleKey,
14675     scope: this
14676 });
14677 </code></pre>
14678      * @param {Object/Array} config A single KeyMap config or an array of configs
14679      */
14680         addBinding : function(config){
14681         if(config instanceof Array){
14682             for(var i = 0, len = config.length; i < len; i++){
14683                 this.addBinding(config[i]);
14684             }
14685             return;
14686         }
14687         var keyCode = config.key,
14688             shift = config.shift, 
14689             ctrl = config.ctrl, 
14690             alt = config.alt,
14691             fn = config.fn,
14692             scope = config.scope;
14693         if(typeof keyCode == "string"){
14694             var ks = [];
14695             var keyString = keyCode.toUpperCase();
14696             for(var j = 0, len = keyString.length; j < len; j++){
14697                 ks.push(keyString.charCodeAt(j));
14698             }
14699             keyCode = ks;
14700         }
14701         var keyArray = keyCode instanceof Array;
14702         var handler = function(e){
14703             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14704                 var k = e.getKey();
14705                 if(keyArray){
14706                     for(var i = 0, len = keyCode.length; i < len; i++){
14707                         if(keyCode[i] == k){
14708                           if(this.stopEvent){
14709                               e.stopEvent();
14710                           }
14711                           fn.call(scope || window, k, e);
14712                           return;
14713                         }
14714                     }
14715                 }else{
14716                     if(k == keyCode){
14717                         if(this.stopEvent){
14718                            e.stopEvent();
14719                         }
14720                         fn.call(scope || window, k, e);
14721                     }
14722                 }
14723             }
14724         };
14725         this.bindings.push(handler);  
14726         },
14727
14728     /**
14729      * Shorthand for adding a single key listener
14730      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14731      * following options:
14732      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14733      * @param {Function} fn The function to call
14734      * @param {Object} scope (optional) The scope of the function
14735      */
14736     on : function(key, fn, scope){
14737         var keyCode, shift, ctrl, alt;
14738         if(typeof key == "object" && !(key instanceof Array)){
14739             keyCode = key.key;
14740             shift = key.shift;
14741             ctrl = key.ctrl;
14742             alt = key.alt;
14743         }else{
14744             keyCode = key;
14745         }
14746         this.addBinding({
14747             key: keyCode,
14748             shift: shift,
14749             ctrl: ctrl,
14750             alt: alt,
14751             fn: fn,
14752             scope: scope
14753         })
14754     },
14755
14756     // private
14757     handleKeyDown : function(e){
14758             if(this.enabled){ //just in case
14759             var b = this.bindings;
14760             for(var i = 0, len = b.length; i < len; i++){
14761                 b[i].call(this, e);
14762             }
14763             }
14764         },
14765         
14766         /**
14767          * Returns true if this KeyMap is enabled
14768          * @return {Boolean} 
14769          */
14770         isEnabled : function(){
14771             return this.enabled;  
14772         },
14773         
14774         /**
14775          * Enables this KeyMap
14776          */
14777         enable: function(){
14778                 if(!this.enabled){
14779                     this.el.on(this.eventName, this.handleKeyDown, this);
14780                     this.enabled = true;
14781                 }
14782         },
14783
14784         /**
14785          * Disable this KeyMap
14786          */
14787         disable: function(){
14788                 if(this.enabled){
14789                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14790                     this.enabled = false;
14791                 }
14792         }
14793 };/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804  
14805 /**
14806  * @class Roo.util.TextMetrics
14807  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14808  * wide, in pixels, a given block of text will be.
14809  * @singleton
14810  */
14811 Roo.util.TextMetrics = function(){
14812     var shared;
14813     return {
14814         /**
14815          * Measures the size of the specified text
14816          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14817          * that can affect the size of the rendered text
14818          * @param {String} text The text to measure
14819          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14820          * in order to accurately measure the text height
14821          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14822          */
14823         measure : function(el, text, fixedWidth){
14824             if(!shared){
14825                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14826             }
14827             shared.bind(el);
14828             shared.setFixedWidth(fixedWidth || 'auto');
14829             return shared.getSize(text);
14830         },
14831
14832         /**
14833          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14834          * the overhead of multiple calls to initialize the style properties on each measurement.
14835          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14836          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14837          * in order to accurately measure the text height
14838          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14839          */
14840         createInstance : function(el, fixedWidth){
14841             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14842         }
14843     };
14844 }();
14845
14846  
14847
14848 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14849     var ml = new Roo.Element(document.createElement('div'));
14850     document.body.appendChild(ml.dom);
14851     ml.position('absolute');
14852     ml.setLeftTop(-1000, -1000);
14853     ml.hide();
14854
14855     if(fixedWidth){
14856         ml.setWidth(fixedWidth);
14857     }
14858      
14859     var instance = {
14860         /**
14861          * Returns the size of the specified text based on the internal element's style and width properties
14862          * @memberOf Roo.util.TextMetrics.Instance#
14863          * @param {String} text The text to measure
14864          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14865          */
14866         getSize : function(text){
14867             ml.update(text);
14868             var s = ml.getSize();
14869             ml.update('');
14870             return s;
14871         },
14872
14873         /**
14874          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14875          * that can affect the size of the rendered text
14876          * @memberOf Roo.util.TextMetrics.Instance#
14877          * @param {String/HTMLElement} el The element, dom node or id
14878          */
14879         bind : function(el){
14880             ml.setStyle(
14881                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14882             );
14883         },
14884
14885         /**
14886          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14887          * to set a fixed width in order to accurately measure the text height.
14888          * @memberOf Roo.util.TextMetrics.Instance#
14889          * @param {Number} width The width to set on the element
14890          */
14891         setFixedWidth : function(width){
14892             ml.setWidth(width);
14893         },
14894
14895         /**
14896          * Returns the measured width of the specified text
14897          * @memberOf Roo.util.TextMetrics.Instance#
14898          * @param {String} text The text to measure
14899          * @return {Number} width The width in pixels
14900          */
14901         getWidth : function(text){
14902             ml.dom.style.width = 'auto';
14903             return this.getSize(text).width;
14904         },
14905
14906         /**
14907          * Returns the measured height of the specified text.  For multiline text, be sure to call
14908          * {@link #setFixedWidth} if necessary.
14909          * @memberOf Roo.util.TextMetrics.Instance#
14910          * @param {String} text The text to measure
14911          * @return {Number} height The height in pixels
14912          */
14913         getHeight : function(text){
14914             return this.getSize(text).height;
14915         }
14916     };
14917
14918     instance.bind(bindTo);
14919
14920     return instance;
14921 };
14922
14923 // backwards compat
14924 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14925  * Based on:
14926  * Ext JS Library 1.1.1
14927  * Copyright(c) 2006-2007, Ext JS, LLC.
14928  *
14929  * Originally Released Under LGPL - original licence link has changed is not relivant.
14930  *
14931  * Fork - LGPL
14932  * <script type="text/javascript">
14933  */
14934
14935 /**
14936  * @class Roo.state.Provider
14937  * Abstract base class for state provider implementations. This class provides methods
14938  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14939  * Provider interface.
14940  */
14941 Roo.state.Provider = function(){
14942     /**
14943      * @event statechange
14944      * Fires when a state change occurs.
14945      * @param {Provider} this This state provider
14946      * @param {String} key The state key which was changed
14947      * @param {String} value The encoded value for the state
14948      */
14949     this.addEvents({
14950         "statechange": true
14951     });
14952     this.state = {};
14953     Roo.state.Provider.superclass.constructor.call(this);
14954 };
14955 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14956     /**
14957      * Returns the current value for a key
14958      * @param {String} name The key name
14959      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14960      * @return {Mixed} The state data
14961      */
14962     get : function(name, defaultValue){
14963         return typeof this.state[name] == "undefined" ?
14964             defaultValue : this.state[name];
14965     },
14966     
14967     /**
14968      * Clears a value from the state
14969      * @param {String} name The key name
14970      */
14971     clear : function(name){
14972         delete this.state[name];
14973         this.fireEvent("statechange", this, name, null);
14974     },
14975     
14976     /**
14977      * Sets the value for a key
14978      * @param {String} name The key name
14979      * @param {Mixed} value The value to set
14980      */
14981     set : function(name, value){
14982         this.state[name] = value;
14983         this.fireEvent("statechange", this, name, value);
14984     },
14985     
14986     /**
14987      * Decodes a string previously encoded with {@link #encodeValue}.
14988      * @param {String} value The value to decode
14989      * @return {Mixed} The decoded value
14990      */
14991     decodeValue : function(cookie){
14992         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14993         var matches = re.exec(unescape(cookie));
14994         if(!matches || !matches[1]) {
14995             return; // non state cookie
14996         }
14997         var type = matches[1];
14998         var v = matches[2];
14999         switch(type){
15000             case "n":
15001                 return parseFloat(v);
15002             case "d":
15003                 return new Date(Date.parse(v));
15004             case "b":
15005                 return (v == "1");
15006             case "a":
15007                 var all = [];
15008                 var values = v.split("^");
15009                 for(var i = 0, len = values.length; i < len; i++){
15010                     all.push(this.decodeValue(values[i]));
15011                 }
15012                 return all;
15013            case "o":
15014                 var all = {};
15015                 var values = v.split("^");
15016                 for(var i = 0, len = values.length; i < len; i++){
15017                     var kv = values[i].split("=");
15018                     all[kv[0]] = this.decodeValue(kv[1]);
15019                 }
15020                 return all;
15021            default:
15022                 return v;
15023         }
15024     },
15025     
15026     /**
15027      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15028      * @param {Mixed} value The value to encode
15029      * @return {String} The encoded value
15030      */
15031     encodeValue : function(v){
15032         var enc;
15033         if(typeof v == "number"){
15034             enc = "n:" + v;
15035         }else if(typeof v == "boolean"){
15036             enc = "b:" + (v ? "1" : "0");
15037         }else if(v instanceof Date){
15038             enc = "d:" + v.toGMTString();
15039         }else if(v instanceof Array){
15040             var flat = "";
15041             for(var i = 0, len = v.length; i < len; i++){
15042                 flat += this.encodeValue(v[i]);
15043                 if(i != len-1) {
15044                     flat += "^";
15045                 }
15046             }
15047             enc = "a:" + flat;
15048         }else if(typeof v == "object"){
15049             var flat = "";
15050             for(var key in v){
15051                 if(typeof v[key] != "function"){
15052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15053                 }
15054             }
15055             enc = "o:" + flat.substring(0, flat.length-1);
15056         }else{
15057             enc = "s:" + v;
15058         }
15059         return escape(enc);        
15060     }
15061 });
15062
15063 /*
15064  * Based on:
15065  * Ext JS Library 1.1.1
15066  * Copyright(c) 2006-2007, Ext JS, LLC.
15067  *
15068  * Originally Released Under LGPL - original licence link has changed is not relivant.
15069  *
15070  * Fork - LGPL
15071  * <script type="text/javascript">
15072  */
15073 /**
15074  * @class Roo.state.Manager
15075  * This is the global state manager. By default all components that are "state aware" check this class
15076  * for state information if you don't pass them a custom state provider. In order for this class
15077  * to be useful, it must be initialized with a provider when your application initializes.
15078  <pre><code>
15079 // in your initialization function
15080 init : function(){
15081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15082    ...
15083    // supposed you have a {@link Roo.BorderLayout}
15084    var layout = new Roo.BorderLayout(...);
15085    layout.restoreState();
15086    // or a {Roo.BasicDialog}
15087    var dialog = new Roo.BasicDialog(...);
15088    dialog.restoreState();
15089  </code></pre>
15090  * @singleton
15091  */
15092 Roo.state.Manager = function(){
15093     var provider = new Roo.state.Provider();
15094     
15095     return {
15096         /**
15097          * Configures the default state provider for your application
15098          * @param {Provider} stateProvider The state provider to set
15099          */
15100         setProvider : function(stateProvider){
15101             provider = stateProvider;
15102         },
15103         
15104         /**
15105          * Returns the current value for a key
15106          * @param {String} name The key name
15107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15108          * @return {Mixed} The state data
15109          */
15110         get : function(key, defaultValue){
15111             return provider.get(key, defaultValue);
15112         },
15113         
15114         /**
15115          * Sets the value for a key
15116          * @param {String} name The key name
15117          * @param {Mixed} value The state data
15118          */
15119          set : function(key, value){
15120             provider.set(key, value);
15121         },
15122         
15123         /**
15124          * Clears a value from the state
15125          * @param {String} name The key name
15126          */
15127         clear : function(key){
15128             provider.clear(key);
15129         },
15130         
15131         /**
15132          * Gets the currently configured state provider
15133          * @return {Provider} The state provider
15134          */
15135         getProvider : function(){
15136             return provider;
15137         }
15138     };
15139 }();
15140 /*
15141  * Based on:
15142  * Ext JS Library 1.1.1
15143  * Copyright(c) 2006-2007, Ext JS, LLC.
15144  *
15145  * Originally Released Under LGPL - original licence link has changed is not relivant.
15146  *
15147  * Fork - LGPL
15148  * <script type="text/javascript">
15149  */
15150 /**
15151  * @class Roo.state.CookieProvider
15152  * @extends Roo.state.Provider
15153  * The default Provider implementation which saves state via cookies.
15154  * <br />Usage:
15155  <pre><code>
15156    var cp = new Roo.state.CookieProvider({
15157        path: "/cgi-bin/",
15158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15159        domain: "roojs.com"
15160    })
15161    Roo.state.Manager.setProvider(cp);
15162  </code></pre>
15163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15168  * domain the page is running on including the 'www' like 'www.roojs.com')
15169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15170  * @constructor
15171  * Create a new CookieProvider
15172  * @param {Object} config The configuration object
15173  */
15174 Roo.state.CookieProvider = function(config){
15175     Roo.state.CookieProvider.superclass.constructor.call(this);
15176     this.path = "/";
15177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15178     this.domain = null;
15179     this.secure = false;
15180     Roo.apply(this, config);
15181     this.state = this.readCookies();
15182 };
15183
15184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15185     // private
15186     set : function(name, value){
15187         if(typeof value == "undefined" || value === null){
15188             this.clear(name);
15189             return;
15190         }
15191         this.setCookie(name, value);
15192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15193     },
15194
15195     // private
15196     clear : function(name){
15197         this.clearCookie(name);
15198         Roo.state.CookieProvider.superclass.clear.call(this, name);
15199     },
15200
15201     // private
15202     readCookies : function(){
15203         var cookies = {};
15204         var c = document.cookie + ";";
15205         var re = /\s?(.*?)=(.*?);/g;
15206         var matches;
15207         while((matches = re.exec(c)) != null){
15208             var name = matches[1];
15209             var value = matches[2];
15210             if(name && name.substring(0,3) == "ys-"){
15211                 cookies[name.substr(3)] = this.decodeValue(value);
15212             }
15213         }
15214         return cookies;
15215     },
15216
15217     // private
15218     setCookie : function(name, value){
15219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15221            ((this.path == null) ? "" : ("; path=" + this.path)) +
15222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15223            ((this.secure == true) ? "; secure" : "");
15224     },
15225
15226     // private
15227     clearCookie : function(name){
15228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15229            ((this.path == null) ? "" : ("; path=" + this.path)) +
15230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15231            ((this.secure == true) ? "; secure" : "");
15232     }
15233 });/*
15234  * Based on:
15235  * Ext JS Library 1.1.1
15236  * Copyright(c) 2006-2007, Ext JS, LLC.
15237  *
15238  * Originally Released Under LGPL - original licence link has changed is not relivant.
15239  *
15240  * Fork - LGPL
15241  * <script type="text/javascript">
15242  */
15243  
15244
15245 /**
15246  * @class Roo.ComponentMgr
15247  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15248  * @singleton
15249  */
15250 Roo.ComponentMgr = function(){
15251     var all = new Roo.util.MixedCollection();
15252
15253     return {
15254         /**
15255          * Registers a component.
15256          * @param {Roo.Component} c The component
15257          */
15258         register : function(c){
15259             all.add(c);
15260         },
15261
15262         /**
15263          * Unregisters a component.
15264          * @param {Roo.Component} c The component
15265          */
15266         unregister : function(c){
15267             all.remove(c);
15268         },
15269
15270         /**
15271          * Returns a component by id
15272          * @param {String} id The component id
15273          */
15274         get : function(id){
15275             return all.get(id);
15276         },
15277
15278         /**
15279          * Registers a function that will be called when a specified component is added to ComponentMgr
15280          * @param {String} id The component id
15281          * @param {Funtction} fn The callback function
15282          * @param {Object} scope The scope of the callback
15283          */
15284         onAvailable : function(id, fn, scope){
15285             all.on("add", function(index, o){
15286                 if(o.id == id){
15287                     fn.call(scope || o, o);
15288                     all.un("add", fn, scope);
15289                 }
15290             });
15291         }
15292     };
15293 }();/*
15294  * Based on:
15295  * Ext JS Library 1.1.1
15296  * Copyright(c) 2006-2007, Ext JS, LLC.
15297  *
15298  * Originally Released Under LGPL - original licence link has changed is not relivant.
15299  *
15300  * Fork - LGPL
15301  * <script type="text/javascript">
15302  */
15303  
15304 /**
15305  * @class Roo.Component
15306  * @extends Roo.util.Observable
15307  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15308  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15309  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15310  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15311  * All visual components (widgets) that require rendering into a layout should subclass Component.
15312  * @constructor
15313  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15314  * 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
15315  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15316  */
15317 Roo.Component = function(config){
15318     config = config || {};
15319     if(config.tagName || config.dom || typeof config == "string"){ // element object
15320         config = {el: config, id: config.id || config};
15321     }
15322     this.initialConfig = config;
15323
15324     Roo.apply(this, config);
15325     this.addEvents({
15326         /**
15327          * @event disable
15328          * Fires after the component is disabled.
15329              * @param {Roo.Component} this
15330              */
15331         disable : true,
15332         /**
15333          * @event enable
15334          * Fires after the component is enabled.
15335              * @param {Roo.Component} this
15336              */
15337         enable : true,
15338         /**
15339          * @event beforeshow
15340          * Fires before the component is shown.  Return false to stop the show.
15341              * @param {Roo.Component} this
15342              */
15343         beforeshow : true,
15344         /**
15345          * @event show
15346          * Fires after the component is shown.
15347              * @param {Roo.Component} this
15348              */
15349         show : true,
15350         /**
15351          * @event beforehide
15352          * Fires before the component is hidden. Return false to stop the hide.
15353              * @param {Roo.Component} this
15354              */
15355         beforehide : true,
15356         /**
15357          * @event hide
15358          * Fires after the component is hidden.
15359              * @param {Roo.Component} this
15360              */
15361         hide : true,
15362         /**
15363          * @event beforerender
15364          * Fires before the component is rendered. Return false to stop the render.
15365              * @param {Roo.Component} this
15366              */
15367         beforerender : true,
15368         /**
15369          * @event render
15370          * Fires after the component is rendered.
15371              * @param {Roo.Component} this
15372              */
15373         render : true,
15374         /**
15375          * @event beforedestroy
15376          * Fires before the component is destroyed. Return false to stop the destroy.
15377              * @param {Roo.Component} this
15378              */
15379         beforedestroy : true,
15380         /**
15381          * @event destroy
15382          * Fires after the component is destroyed.
15383              * @param {Roo.Component} this
15384              */
15385         destroy : true
15386     });
15387     if(!this.id){
15388         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15389     }
15390     Roo.ComponentMgr.register(this);
15391     Roo.Component.superclass.constructor.call(this);
15392     this.initComponent();
15393     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15394         this.render(this.renderTo);
15395         delete this.renderTo;
15396     }
15397 };
15398
15399 /** @private */
15400 Roo.Component.AUTO_ID = 1000;
15401
15402 Roo.extend(Roo.Component, Roo.util.Observable, {
15403     /**
15404      * @scope Roo.Component.prototype
15405      * @type {Boolean}
15406      * true if this component is hidden. Read-only.
15407      */
15408     hidden : false,
15409     /**
15410      * @type {Boolean}
15411      * true if this component is disabled. Read-only.
15412      */
15413     disabled : false,
15414     /**
15415      * @type {Boolean}
15416      * true if this component has been rendered. Read-only.
15417      */
15418     rendered : false,
15419     
15420     /** @cfg {String} disableClass
15421      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15422      */
15423     disabledClass : "x-item-disabled",
15424         /** @cfg {Boolean} allowDomMove
15425          * Whether the component can move the Dom node when rendering (defaults to true).
15426          */
15427     allowDomMove : true,
15428     /** @cfg {String} hideMode (display|visibility)
15429      * How this component should hidden. Supported values are
15430      * "visibility" (css visibility), "offsets" (negative offset position) and
15431      * "display" (css display) - defaults to "display".
15432      */
15433     hideMode: 'display',
15434
15435     /** @private */
15436     ctype : "Roo.Component",
15437
15438     /**
15439      * @cfg {String} actionMode 
15440      * which property holds the element that used for  hide() / show() / disable() / enable()
15441      * default is 'el' 
15442      */
15443     actionMode : "el",
15444
15445     /** @private */
15446     getActionEl : function(){
15447         return this[this.actionMode];
15448     },
15449
15450     initComponent : Roo.emptyFn,
15451     /**
15452      * If this is a lazy rendering component, render it to its container element.
15453      * @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.
15454      */
15455     render : function(container, position){
15456         
15457         if(this.rendered){
15458             return this;
15459         }
15460         
15461         if(this.fireEvent("beforerender", this) === false){
15462             return false;
15463         }
15464         
15465         if(!container && this.el){
15466             this.el = Roo.get(this.el);
15467             container = this.el.dom.parentNode;
15468             this.allowDomMove = false;
15469         }
15470         this.container = Roo.get(container);
15471         this.rendered = true;
15472         if(position !== undefined){
15473             if(typeof position == 'number'){
15474                 position = this.container.dom.childNodes[position];
15475             }else{
15476                 position = Roo.getDom(position);
15477             }
15478         }
15479         this.onRender(this.container, position || null);
15480         if(this.cls){
15481             this.el.addClass(this.cls);
15482             delete this.cls;
15483         }
15484         if(this.style){
15485             this.el.applyStyles(this.style);
15486             delete this.style;
15487         }
15488         this.fireEvent("render", this);
15489         this.afterRender(this.container);
15490         if(this.hidden){
15491             this.hide();
15492         }
15493         if(this.disabled){
15494             this.disable();
15495         }
15496
15497         return this;
15498         
15499     },
15500
15501     /** @private */
15502     // default function is not really useful
15503     onRender : function(ct, position){
15504         if(this.el){
15505             this.el = Roo.get(this.el);
15506             if(this.allowDomMove !== false){
15507                 ct.dom.insertBefore(this.el.dom, position);
15508             }
15509         }
15510     },
15511
15512     /** @private */
15513     getAutoCreate : function(){
15514         var cfg = typeof this.autoCreate == "object" ?
15515                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15516         if(this.id && !cfg.id){
15517             cfg.id = this.id;
15518         }
15519         return cfg;
15520     },
15521
15522     /** @private */
15523     afterRender : Roo.emptyFn,
15524
15525     /**
15526      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15527      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15528      */
15529     destroy : function(){
15530         if(this.fireEvent("beforedestroy", this) !== false){
15531             this.purgeListeners();
15532             this.beforeDestroy();
15533             if(this.rendered){
15534                 this.el.removeAllListeners();
15535                 this.el.remove();
15536                 if(this.actionMode == "container"){
15537                     this.container.remove();
15538                 }
15539             }
15540             this.onDestroy();
15541             Roo.ComponentMgr.unregister(this);
15542             this.fireEvent("destroy", this);
15543         }
15544     },
15545
15546         /** @private */
15547     beforeDestroy : function(){
15548
15549     },
15550
15551         /** @private */
15552         onDestroy : function(){
15553
15554     },
15555
15556     /**
15557      * Returns the underlying {@link Roo.Element}.
15558      * @return {Roo.Element} The element
15559      */
15560     getEl : function(){
15561         return this.el;
15562     },
15563
15564     /**
15565      * Returns the id of this component.
15566      * @return {String}
15567      */
15568     getId : function(){
15569         return this.id;
15570     },
15571
15572     /**
15573      * Try to focus this component.
15574      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15575      * @return {Roo.Component} this
15576      */
15577     focus : function(selectText){
15578         if(this.rendered){
15579             this.el.focus();
15580             if(selectText === true){
15581                 this.el.dom.select();
15582             }
15583         }
15584         return this;
15585     },
15586
15587     /** @private */
15588     blur : function(){
15589         if(this.rendered){
15590             this.el.blur();
15591         }
15592         return this;
15593     },
15594
15595     /**
15596      * Disable this component.
15597      * @return {Roo.Component} this
15598      */
15599     disable : function(){
15600         if(this.rendered){
15601             this.onDisable();
15602         }
15603         this.disabled = true;
15604         this.fireEvent("disable", this);
15605         return this;
15606     },
15607
15608         // private
15609     onDisable : function(){
15610         this.getActionEl().addClass(this.disabledClass);
15611         this.el.dom.disabled = true;
15612     },
15613
15614     /**
15615      * Enable this component.
15616      * @return {Roo.Component} this
15617      */
15618     enable : function(){
15619         if(this.rendered){
15620             this.onEnable();
15621         }
15622         this.disabled = false;
15623         this.fireEvent("enable", this);
15624         return this;
15625     },
15626
15627         // private
15628     onEnable : function(){
15629         this.getActionEl().removeClass(this.disabledClass);
15630         this.el.dom.disabled = false;
15631     },
15632
15633     /**
15634      * Convenience function for setting disabled/enabled by boolean.
15635      * @param {Boolean} disabled
15636      */
15637     setDisabled : function(disabled){
15638         this[disabled ? "disable" : "enable"]();
15639     },
15640
15641     /**
15642      * Show this component.
15643      * @return {Roo.Component} this
15644      */
15645     show: function(){
15646         if(this.fireEvent("beforeshow", this) !== false){
15647             this.hidden = false;
15648             if(this.rendered){
15649                 this.onShow();
15650             }
15651             this.fireEvent("show", this);
15652         }
15653         return this;
15654     },
15655
15656     // private
15657     onShow : function(){
15658         var ae = this.getActionEl();
15659         if(this.hideMode == 'visibility'){
15660             ae.dom.style.visibility = "visible";
15661         }else if(this.hideMode == 'offsets'){
15662             ae.removeClass('x-hidden');
15663         }else{
15664             ae.dom.style.display = "";
15665         }
15666     },
15667
15668     /**
15669      * Hide this component.
15670      * @return {Roo.Component} this
15671      */
15672     hide: function(){
15673         if(this.fireEvent("beforehide", this) !== false){
15674             this.hidden = true;
15675             if(this.rendered){
15676                 this.onHide();
15677             }
15678             this.fireEvent("hide", this);
15679         }
15680         return this;
15681     },
15682
15683     // private
15684     onHide : function(){
15685         var ae = this.getActionEl();
15686         if(this.hideMode == 'visibility'){
15687             ae.dom.style.visibility = "hidden";
15688         }else if(this.hideMode == 'offsets'){
15689             ae.addClass('x-hidden');
15690         }else{
15691             ae.dom.style.display = "none";
15692         }
15693     },
15694
15695     /**
15696      * Convenience function to hide or show this component by boolean.
15697      * @param {Boolean} visible True to show, false to hide
15698      * @return {Roo.Component} this
15699      */
15700     setVisible: function(visible){
15701         if(visible) {
15702             this.show();
15703         }else{
15704             this.hide();
15705         }
15706         return this;
15707     },
15708
15709     /**
15710      * Returns true if this component is visible.
15711      */
15712     isVisible : function(){
15713         return this.getActionEl().isVisible();
15714     },
15715
15716     cloneConfig : function(overrides){
15717         overrides = overrides || {};
15718         var id = overrides.id || Roo.id();
15719         var cfg = Roo.applyIf(overrides, this.initialConfig);
15720         cfg.id = id; // prevent dup id
15721         return new this.constructor(cfg);
15722     }
15723 });/*
15724  * Based on:
15725  * Ext JS Library 1.1.1
15726  * Copyright(c) 2006-2007, Ext JS, LLC.
15727  *
15728  * Originally Released Under LGPL - original licence link has changed is not relivant.
15729  *
15730  * Fork - LGPL
15731  * <script type="text/javascript">
15732  */
15733
15734 /**
15735  * @class Roo.BoxComponent
15736  * @extends Roo.Component
15737  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15738  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15739  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15740  * layout containers.
15741  * @constructor
15742  * @param {Roo.Element/String/Object} config The configuration options.
15743  */
15744 Roo.BoxComponent = function(config){
15745     Roo.Component.call(this, config);
15746     this.addEvents({
15747         /**
15748          * @event resize
15749          * Fires after the component is resized.
15750              * @param {Roo.Component} this
15751              * @param {Number} adjWidth The box-adjusted width that was set
15752              * @param {Number} adjHeight The box-adjusted height that was set
15753              * @param {Number} rawWidth The width that was originally specified
15754              * @param {Number} rawHeight The height that was originally specified
15755              */
15756         resize : true,
15757         /**
15758          * @event move
15759          * Fires after the component is moved.
15760              * @param {Roo.Component} this
15761              * @param {Number} x The new x position
15762              * @param {Number} y The new y position
15763              */
15764         move : true
15765     });
15766 };
15767
15768 Roo.extend(Roo.BoxComponent, Roo.Component, {
15769     // private, set in afterRender to signify that the component has been rendered
15770     boxReady : false,
15771     // private, used to defer height settings to subclasses
15772     deferHeight: false,
15773     /** @cfg {Number} width
15774      * width (optional) size of component
15775      */
15776      /** @cfg {Number} height
15777      * height (optional) size of component
15778      */
15779      
15780     /**
15781      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15782      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15783      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15784      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15785      * @return {Roo.BoxComponent} this
15786      */
15787     setSize : function(w, h){
15788         // support for standard size objects
15789         if(typeof w == 'object'){
15790             h = w.height;
15791             w = w.width;
15792         }
15793         // not rendered
15794         if(!this.boxReady){
15795             this.width = w;
15796             this.height = h;
15797             return this;
15798         }
15799
15800         // prevent recalcs when not needed
15801         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15802             return this;
15803         }
15804         this.lastSize = {width: w, height: h};
15805
15806         var adj = this.adjustSize(w, h);
15807         var aw = adj.width, ah = adj.height;
15808         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15809             var rz = this.getResizeEl();
15810             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15811                 rz.setSize(aw, ah);
15812             }else if(!this.deferHeight && ah !== undefined){
15813                 rz.setHeight(ah);
15814             }else if(aw !== undefined){
15815                 rz.setWidth(aw);
15816             }
15817             this.onResize(aw, ah, w, h);
15818             this.fireEvent('resize', this, aw, ah, w, h);
15819         }
15820         return this;
15821     },
15822
15823     /**
15824      * Gets the current size of the component's underlying element.
15825      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15826      */
15827     getSize : function(){
15828         return this.el.getSize();
15829     },
15830
15831     /**
15832      * Gets the current XY position of the component's underlying element.
15833      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15834      * @return {Array} The XY position of the element (e.g., [100, 200])
15835      */
15836     getPosition : function(local){
15837         if(local === true){
15838             return [this.el.getLeft(true), this.el.getTop(true)];
15839         }
15840         return this.xy || this.el.getXY();
15841     },
15842
15843     /**
15844      * Gets the current box measurements of the component's underlying element.
15845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15846      * @returns {Object} box An object in the format {x, y, width, height}
15847      */
15848     getBox : function(local){
15849         var s = this.el.getSize();
15850         if(local){
15851             s.x = this.el.getLeft(true);
15852             s.y = this.el.getTop(true);
15853         }else{
15854             var xy = this.xy || this.el.getXY();
15855             s.x = xy[0];
15856             s.y = xy[1];
15857         }
15858         return s;
15859     },
15860
15861     /**
15862      * Sets the current box measurements of the component's underlying element.
15863      * @param {Object} box An object in the format {x, y, width, height}
15864      * @returns {Roo.BoxComponent} this
15865      */
15866     updateBox : function(box){
15867         this.setSize(box.width, box.height);
15868         this.setPagePosition(box.x, box.y);
15869         return this;
15870     },
15871
15872     // protected
15873     getResizeEl : function(){
15874         return this.resizeEl || this.el;
15875     },
15876
15877     // protected
15878     getPositionEl : function(){
15879         return this.positionEl || this.el;
15880     },
15881
15882     /**
15883      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15884      * This method fires the move event.
15885      * @param {Number} left The new left
15886      * @param {Number} top The new top
15887      * @returns {Roo.BoxComponent} this
15888      */
15889     setPosition : function(x, y){
15890         this.x = x;
15891         this.y = y;
15892         if(!this.boxReady){
15893             return this;
15894         }
15895         var adj = this.adjustPosition(x, y);
15896         var ax = adj.x, ay = adj.y;
15897
15898         var el = this.getPositionEl();
15899         if(ax !== undefined || ay !== undefined){
15900             if(ax !== undefined && ay !== undefined){
15901                 el.setLeftTop(ax, ay);
15902             }else if(ax !== undefined){
15903                 el.setLeft(ax);
15904             }else if(ay !== undefined){
15905                 el.setTop(ay);
15906             }
15907             this.onPosition(ax, ay);
15908             this.fireEvent('move', this, ax, ay);
15909         }
15910         return this;
15911     },
15912
15913     /**
15914      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15915      * This method fires the move event.
15916      * @param {Number} x The new x position
15917      * @param {Number} y The new y position
15918      * @returns {Roo.BoxComponent} this
15919      */
15920     setPagePosition : function(x, y){
15921         this.pageX = x;
15922         this.pageY = y;
15923         if(!this.boxReady){
15924             return;
15925         }
15926         if(x === undefined || y === undefined){ // cannot translate undefined points
15927             return;
15928         }
15929         var p = this.el.translatePoints(x, y);
15930         this.setPosition(p.left, p.top);
15931         return this;
15932     },
15933
15934     // private
15935     onRender : function(ct, position){
15936         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15937         if(this.resizeEl){
15938             this.resizeEl = Roo.get(this.resizeEl);
15939         }
15940         if(this.positionEl){
15941             this.positionEl = Roo.get(this.positionEl);
15942         }
15943     },
15944
15945     // private
15946     afterRender : function(){
15947         Roo.BoxComponent.superclass.afterRender.call(this);
15948         this.boxReady = true;
15949         this.setSize(this.width, this.height);
15950         if(this.x || this.y){
15951             this.setPosition(this.x, this.y);
15952         }
15953         if(this.pageX || this.pageY){
15954             this.setPagePosition(this.pageX, this.pageY);
15955         }
15956     },
15957
15958     /**
15959      * Force the component's size to recalculate based on the underlying element's current height and width.
15960      * @returns {Roo.BoxComponent} this
15961      */
15962     syncSize : function(){
15963         delete this.lastSize;
15964         this.setSize(this.el.getWidth(), this.el.getHeight());
15965         return this;
15966     },
15967
15968     /**
15969      * Called after the component is resized, this method is empty by default but can be implemented by any
15970      * subclass that needs to perform custom logic after a resize occurs.
15971      * @param {Number} adjWidth The box-adjusted width that was set
15972      * @param {Number} adjHeight The box-adjusted height that was set
15973      * @param {Number} rawWidth The width that was originally specified
15974      * @param {Number} rawHeight The height that was originally specified
15975      */
15976     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15977
15978     },
15979
15980     /**
15981      * Called after the component is moved, this method is empty by default but can be implemented by any
15982      * subclass that needs to perform custom logic after a move occurs.
15983      * @param {Number} x The new x position
15984      * @param {Number} y The new y position
15985      */
15986     onPosition : function(x, y){
15987
15988     },
15989
15990     // private
15991     adjustSize : function(w, h){
15992         if(this.autoWidth){
15993             w = 'auto';
15994         }
15995         if(this.autoHeight){
15996             h = 'auto';
15997         }
15998         return {width : w, height: h};
15999     },
16000
16001     // private
16002     adjustPosition : function(x, y){
16003         return {x : x, y: y};
16004     }
16005 });/*
16006  * Original code for Roojs - LGPL
16007  * <script type="text/javascript">
16008  */
16009  
16010 /**
16011  * @class Roo.XComponent
16012  * A delayed Element creator...
16013  * Or a way to group chunks of interface together.
16014  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16015  *  used in conjunction with XComponent.build() it will create an instance of each element,
16016  *  then call addxtype() to build the User interface.
16017  * 
16018  * Mypart.xyx = new Roo.XComponent({
16019
16020     parent : 'Mypart.xyz', // empty == document.element.!!
16021     order : '001',
16022     name : 'xxxx'
16023     region : 'xxxx'
16024     disabled : function() {} 
16025      
16026     tree : function() { // return an tree of xtype declared components
16027         var MODULE = this;
16028         return 
16029         {
16030             xtype : 'NestedLayoutPanel',
16031             // technicall
16032         }
16033      ]
16034  *})
16035  *
16036  *
16037  * It can be used to build a big heiracy, with parent etc.
16038  * or you can just use this to render a single compoent to a dom element
16039  * MYPART.render(Roo.Element | String(id) | dom_element )
16040  *
16041  *
16042  * Usage patterns.
16043  *
16044  * Classic Roo
16045  *
16046  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16047  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16048  *
16049  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16050  *
16051  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16052  * - if mulitple topModules exist, the last one is defined as the top module.
16053  *
16054  * Embeded Roo
16055  * 
16056  * When the top level or multiple modules are to embedded into a existing HTML page,
16057  * the parent element can container '#id' of the element where the module will be drawn.
16058  *
16059  * Bootstrap Roo
16060  *
16061  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16062  * it relies more on a include mechanism, where sub modules are included into an outer page.
16063  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16064  * 
16065  * Bootstrap Roo Included elements
16066  *
16067  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16068  * hence confusing the component builder as it thinks there are multiple top level elements. 
16069  *
16070  * String Over-ride & Translations
16071  *
16072  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16073  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16074  * are needed. @see Roo.XComponent.overlayString  
16075  * 
16076  * 
16077  * 
16078  * @extends Roo.util.Observable
16079  * @constructor
16080  * @param cfg {Object} configuration of component
16081  * 
16082  */
16083 Roo.XComponent = function(cfg) {
16084     Roo.apply(this, cfg);
16085     this.addEvents({ 
16086         /**
16087              * @event built
16088              * Fires when this the componnt is built
16089              * @param {Roo.XComponent} c the component
16090              */
16091         'built' : true
16092         
16093     });
16094     this.region = this.region || 'center'; // default..
16095     Roo.XComponent.register(this);
16096     this.modules = false;
16097     this.el = false; // where the layout goes..
16098     
16099     
16100 }
16101 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16102     /**
16103      * @property el
16104      * The created element (with Roo.factory())
16105      * @type {Roo.Layout}
16106      */
16107     el  : false,
16108     
16109     /**
16110      * @property el
16111      * for BC  - use el in new code
16112      * @type {Roo.Layout}
16113      */
16114     panel : false,
16115     
16116     /**
16117      * @property layout
16118      * for BC  - use el in new code
16119      * @type {Roo.Layout}
16120      */
16121     layout : false,
16122     
16123      /**
16124      * @cfg {Function|boolean} disabled
16125      * If this module is disabled by some rule, return true from the funtion
16126      */
16127     disabled : false,
16128     
16129     /**
16130      * @cfg {String} parent 
16131      * Name of parent element which it get xtype added to..
16132      */
16133     parent: false,
16134     
16135     /**
16136      * @cfg {String} order
16137      * Used to set the order in which elements are created (usefull for multiple tabs)
16138      */
16139     
16140     order : false,
16141     /**
16142      * @cfg {String} name
16143      * String to display while loading.
16144      */
16145     name : false,
16146     /**
16147      * @cfg {String} region
16148      * Region to render component to (defaults to center)
16149      */
16150     region : 'center',
16151     
16152     /**
16153      * @cfg {Array} items
16154      * A single item array - the first element is the root of the tree..
16155      * It's done this way to stay compatible with the Xtype system...
16156      */
16157     items : false,
16158     
16159     /**
16160      * @property _tree
16161      * The method that retuns the tree of parts that make up this compoennt 
16162      * @type {function}
16163      */
16164     _tree  : false,
16165     
16166      /**
16167      * render
16168      * render element to dom or tree
16169      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16170      */
16171     
16172     render : function(el)
16173     {
16174         
16175         el = el || false;
16176         var hp = this.parent ? 1 : 0;
16177         Roo.debug &&  Roo.log(this);
16178         
16179         var tree = this._tree ? this._tree() : this.tree();
16180
16181         
16182         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16183             // if parent is a '#.....' string, then let's use that..
16184             var ename = this.parent.substr(1);
16185             this.parent = false;
16186             Roo.debug && Roo.log(ename);
16187             switch (ename) {
16188                 case 'bootstrap-body':
16189                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16190                         // this is the BorderLayout standard?
16191                        this.parent = { el : true };
16192                        break;
16193                     }
16194                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16195                         // need to insert stuff...
16196                         this.parent =  {
16197                              el : new Roo.bootstrap.layout.Border({
16198                                  el : document.body, 
16199                      
16200                                  center: {
16201                                     titlebar: false,
16202                                     autoScroll:false,
16203                                     closeOnTab: true,
16204                                     tabPosition: 'top',
16205                                       //resizeTabs: true,
16206                                     alwaysShowTabs: true,
16207                                     hideTabs: false
16208                                      //minTabWidth: 140
16209                                  }
16210                              })
16211                         
16212                          };
16213                          break;
16214                     }
16215                          
16216                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16217                         this.parent = { el :  new  Roo.bootstrap.Body() };
16218                         Roo.debug && Roo.log("setting el to doc body");
16219                          
16220                     } else {
16221                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16222                     }
16223                     break;
16224                 case 'bootstrap':
16225                     this.parent = { el : true};
16226                     // fall through
16227                 default:
16228                     el = Roo.get(ename);
16229                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16230                         this.parent = { el : true};
16231                     }
16232                     
16233                     break;
16234             }
16235                 
16236             
16237             if (!el && !this.parent) {
16238                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16239                 return;
16240             }
16241         }
16242         
16243         Roo.debug && Roo.log("EL:");
16244         Roo.debug && Roo.log(el);
16245         Roo.debug && Roo.log("this.parent.el:");
16246         Roo.debug && Roo.log(this.parent.el);
16247         
16248
16249         // altertive root elements ??? - we need a better way to indicate these.
16250         var is_alt = Roo.XComponent.is_alt ||
16251                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16252                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16253                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16254         
16255         
16256         
16257         if (!this.parent && is_alt) {
16258             //el = Roo.get(document.body);
16259             this.parent = { el : true };
16260         }
16261             
16262             
16263         
16264         if (!this.parent) {
16265             
16266             Roo.debug && Roo.log("no parent - creating one");
16267             
16268             el = el ? Roo.get(el) : false;      
16269             
16270             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16271                 
16272                 this.parent =  {
16273                     el : new Roo.bootstrap.layout.Border({
16274                         el: el || document.body,
16275                     
16276                         center: {
16277                             titlebar: false,
16278                             autoScroll:false,
16279                             closeOnTab: true,
16280                             tabPosition: 'top',
16281                              //resizeTabs: true,
16282                             alwaysShowTabs: false,
16283                             hideTabs: true,
16284                             minTabWidth: 140,
16285                             overflow: 'visible'
16286                          }
16287                      })
16288                 };
16289             } else {
16290             
16291                 // it's a top level one..
16292                 this.parent =  {
16293                     el : new Roo.BorderLayout(el || document.body, {
16294                         center: {
16295                             titlebar: false,
16296                             autoScroll:false,
16297                             closeOnTab: true,
16298                             tabPosition: 'top',
16299                              //resizeTabs: true,
16300                             alwaysShowTabs: el && hp? false :  true,
16301                             hideTabs: el || !hp ? true :  false,
16302                             minTabWidth: 140
16303                          }
16304                     })
16305                 };
16306             }
16307         }
16308         
16309         if (!this.parent.el) {
16310                 // probably an old style ctor, which has been disabled.
16311                 return;
16312
16313         }
16314                 // The 'tree' method is  '_tree now' 
16315             
16316         tree.region = tree.region || this.region;
16317         var is_body = false;
16318         if (this.parent.el === true) {
16319             // bootstrap... - body..
16320             if (el) {
16321                 tree.el = el;
16322             }
16323             this.parent.el = Roo.factory(tree);
16324             is_body = true;
16325         }
16326         
16327         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16328         this.fireEvent('built', this);
16329         
16330         this.panel = this.el;
16331         this.layout = this.panel.layout;
16332         this.parentLayout = this.parent.layout  || false;  
16333          
16334     }
16335     
16336 });
16337
16338 Roo.apply(Roo.XComponent, {
16339     /**
16340      * @property  hideProgress
16341      * true to disable the building progress bar.. usefull on single page renders.
16342      * @type Boolean
16343      */
16344     hideProgress : false,
16345     /**
16346      * @property  buildCompleted
16347      * True when the builder has completed building the interface.
16348      * @type Boolean
16349      */
16350     buildCompleted : false,
16351      
16352     /**
16353      * @property  topModule
16354      * the upper most module - uses document.element as it's constructor.
16355      * @type Object
16356      */
16357      
16358     topModule  : false,
16359       
16360     /**
16361      * @property  modules
16362      * array of modules to be created by registration system.
16363      * @type {Array} of Roo.XComponent
16364      */
16365     
16366     modules : [],
16367     /**
16368      * @property  elmodules
16369      * array of modules to be created by which use #ID 
16370      * @type {Array} of Roo.XComponent
16371      */
16372      
16373     elmodules : [],
16374
16375      /**
16376      * @property  is_alt
16377      * Is an alternative Root - normally used by bootstrap or other systems,
16378      *    where the top element in the tree can wrap 'body' 
16379      * @type {boolean}  (default false)
16380      */
16381      
16382     is_alt : false,
16383     /**
16384      * @property  build_from_html
16385      * Build elements from html - used by bootstrap HTML stuff 
16386      *    - this is cleared after build is completed
16387      * @type {boolean}    (default false)
16388      */
16389      
16390     build_from_html : false,
16391     /**
16392      * Register components to be built later.
16393      *
16394      * This solves the following issues
16395      * - Building is not done on page load, but after an authentication process has occured.
16396      * - Interface elements are registered on page load
16397      * - Parent Interface elements may not be loaded before child, so this handles that..
16398      * 
16399      *
16400      * example:
16401      * 
16402      * MyApp.register({
16403           order : '000001',
16404           module : 'Pman.Tab.projectMgr',
16405           region : 'center',
16406           parent : 'Pman.layout',
16407           disabled : false,  // or use a function..
16408         })
16409      
16410      * * @param {Object} details about module
16411      */
16412     register : function(obj) {
16413                 
16414         Roo.XComponent.event.fireEvent('register', obj);
16415         switch(typeof(obj.disabled) ) {
16416                 
16417             case 'undefined':
16418                 break;
16419             
16420             case 'function':
16421                 if ( obj.disabled() ) {
16422                         return;
16423                 }
16424                 break;
16425             
16426             default:
16427                 if (obj.disabled || obj.region == '#disabled') {
16428                         return;
16429                 }
16430                 break;
16431         }
16432                 
16433         this.modules.push(obj);
16434          
16435     },
16436     /**
16437      * convert a string to an object..
16438      * eg. 'AAA.BBB' -> finds AAA.BBB
16439
16440      */
16441     
16442     toObject : function(str)
16443     {
16444         if (!str || typeof(str) == 'object') {
16445             return str;
16446         }
16447         if (str.substring(0,1) == '#') {
16448             return str;
16449         }
16450
16451         var ar = str.split('.');
16452         var rt, o;
16453         rt = ar.shift();
16454             /** eval:var:o */
16455         try {
16456             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16457         } catch (e) {
16458             throw "Module not found : " + str;
16459         }
16460         
16461         if (o === false) {
16462             throw "Module not found : " + str;
16463         }
16464         Roo.each(ar, function(e) {
16465             if (typeof(o[e]) == 'undefined') {
16466                 throw "Module not found : " + str;
16467             }
16468             o = o[e];
16469         });
16470         
16471         return o;
16472         
16473     },
16474     
16475     
16476     /**
16477      * move modules into their correct place in the tree..
16478      * 
16479      */
16480     preBuild : function ()
16481     {
16482         var _t = this;
16483         Roo.each(this.modules , function (obj)
16484         {
16485             Roo.XComponent.event.fireEvent('beforebuild', obj);
16486             
16487             var opar = obj.parent;
16488             try { 
16489                 obj.parent = this.toObject(opar);
16490             } catch(e) {
16491                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16492                 return;
16493             }
16494             
16495             if (!obj.parent) {
16496                 Roo.debug && Roo.log("GOT top level module");
16497                 Roo.debug && Roo.log(obj);
16498                 obj.modules = new Roo.util.MixedCollection(false, 
16499                     function(o) { return o.order + '' }
16500                 );
16501                 this.topModule = obj;
16502                 return;
16503             }
16504                         // parent is a string (usually a dom element name..)
16505             if (typeof(obj.parent) == 'string') {
16506                 this.elmodules.push(obj);
16507                 return;
16508             }
16509             if (obj.parent.constructor != Roo.XComponent) {
16510                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16511             }
16512             if (!obj.parent.modules) {
16513                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16514                     function(o) { return o.order + '' }
16515                 );
16516             }
16517             if (obj.parent.disabled) {
16518                 obj.disabled = true;
16519             }
16520             obj.parent.modules.add(obj);
16521         }, this);
16522     },
16523     
16524      /**
16525      * make a list of modules to build.
16526      * @return {Array} list of modules. 
16527      */ 
16528     
16529     buildOrder : function()
16530     {
16531         var _this = this;
16532         var cmp = function(a,b) {   
16533             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16534         };
16535         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16536             throw "No top level modules to build";
16537         }
16538         
16539         // make a flat list in order of modules to build.
16540         var mods = this.topModule ? [ this.topModule ] : [];
16541                 
16542         
16543         // elmodules (is a list of DOM based modules )
16544         Roo.each(this.elmodules, function(e) {
16545             mods.push(e);
16546             if (!this.topModule &&
16547                 typeof(e.parent) == 'string' &&
16548                 e.parent.substring(0,1) == '#' &&
16549                 Roo.get(e.parent.substr(1))
16550                ) {
16551                 
16552                 _this.topModule = e;
16553             }
16554             
16555         });
16556
16557         
16558         // add modules to their parents..
16559         var addMod = function(m) {
16560             Roo.debug && Roo.log("build Order: add: " + m.name);
16561                 
16562             mods.push(m);
16563             if (m.modules && !m.disabled) {
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16565                 m.modules.keySort('ASC',  cmp );
16566                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16567     
16568                 m.modules.each(addMod);
16569             } else {
16570                 Roo.debug && Roo.log("build Order: no child modules");
16571             }
16572             // not sure if this is used any more..
16573             if (m.finalize) {
16574                 m.finalize.name = m.name + " (clean up) ";
16575                 mods.push(m.finalize);
16576             }
16577             
16578         }
16579         if (this.topModule && this.topModule.modules) { 
16580             this.topModule.modules.keySort('ASC',  cmp );
16581             this.topModule.modules.each(addMod);
16582         } 
16583         return mods;
16584     },
16585     
16586      /**
16587      * Build the registered modules.
16588      * @param {Object} parent element.
16589      * @param {Function} optional method to call after module has been added.
16590      * 
16591      */ 
16592    
16593     build : function(opts) 
16594     {
16595         
16596         if (typeof(opts) != 'undefined') {
16597             Roo.apply(this,opts);
16598         }
16599         
16600         this.preBuild();
16601         var mods = this.buildOrder();
16602       
16603         //this.allmods = mods;
16604         //Roo.debug && Roo.log(mods);
16605         //return;
16606         if (!mods.length) { // should not happen
16607             throw "NO modules!!!";
16608         }
16609         
16610         
16611         var msg = "Building Interface...";
16612         // flash it up as modal - so we store the mask!?
16613         if (!this.hideProgress && Roo.MessageBox) {
16614             Roo.MessageBox.show({ title: 'loading' });
16615             Roo.MessageBox.show({
16616                title: "Please wait...",
16617                msg: msg,
16618                width:450,
16619                progress:true,
16620                buttons : false,
16621                closable:false,
16622                modal: false
16623               
16624             });
16625         }
16626         var total = mods.length;
16627         
16628         var _this = this;
16629         var progressRun = function() {
16630             if (!mods.length) {
16631                 Roo.debug && Roo.log('hide?');
16632                 if (!this.hideProgress && Roo.MessageBox) {
16633                     Roo.MessageBox.hide();
16634                 }
16635                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16636                 
16637                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16638                 
16639                 // THE END...
16640                 return false;   
16641             }
16642             
16643             var m = mods.shift();
16644             
16645             
16646             Roo.debug && Roo.log(m);
16647             // not sure if this is supported any more.. - modules that are are just function
16648             if (typeof(m) == 'function') { 
16649                 m.call(this);
16650                 return progressRun.defer(10, _this);
16651             } 
16652             
16653             
16654             msg = "Building Interface " + (total  - mods.length) + 
16655                     " of " + total + 
16656                     (m.name ? (' - ' + m.name) : '');
16657                         Roo.debug && Roo.log(msg);
16658             if (!_this.hideProgress &&  Roo.MessageBox) { 
16659                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16660             }
16661             
16662          
16663             // is the module disabled?
16664             var disabled = (typeof(m.disabled) == 'function') ?
16665                 m.disabled.call(m.module.disabled) : m.disabled;    
16666             
16667             
16668             if (disabled) {
16669                 return progressRun(); // we do not update the display!
16670             }
16671             
16672             // now build 
16673             
16674                         
16675                         
16676             m.render();
16677             // it's 10 on top level, and 1 on others??? why...
16678             return progressRun.defer(10, _this);
16679              
16680         }
16681         progressRun.defer(1, _this);
16682      
16683         
16684         
16685     },
16686     /**
16687      * Overlay a set of modified strings onto a component
16688      * This is dependant on our builder exporting the strings and 'named strings' elements.
16689      * 
16690      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16691      * @param {Object} associative array of 'named' string and it's new value.
16692      * 
16693      */
16694         overlayStrings : function( component, strings )
16695     {
16696         if (typeof(component['_named_strings']) == 'undefined') {
16697             throw "ERROR: component does not have _named_strings";
16698         }
16699         for ( var k in strings ) {
16700             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16701             if (md !== false) {
16702                 component['_strings'][md] = strings[k];
16703             } else {
16704                 Roo.log('could not find named string: ' + k + ' in');
16705                 Roo.log(component);
16706             }
16707             
16708         }
16709         
16710     },
16711     
16712         
16713         /**
16714          * Event Object.
16715          *
16716          *
16717          */
16718         event: false, 
16719     /**
16720          * wrapper for event.on - aliased later..  
16721          * Typically use to register a event handler for register:
16722          *
16723          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16724          *
16725          */
16726     on : false
16727    
16728     
16729     
16730 });
16731
16732 Roo.XComponent.event = new Roo.util.Observable({
16733                 events : { 
16734                         /**
16735                          * @event register
16736                          * Fires when an Component is registered,
16737                          * set the disable property on the Component to stop registration.
16738                          * @param {Roo.XComponent} c the component being registerd.
16739                          * 
16740                          */
16741                         'register' : true,
16742             /**
16743                          * @event beforebuild
16744                          * Fires before each Component is built
16745                          * can be used to apply permissions.
16746                          * @param {Roo.XComponent} c the component being registerd.
16747                          * 
16748                          */
16749                         'beforebuild' : true,
16750                         /**
16751                          * @event buildcomplete
16752                          * Fires on the top level element when all elements have been built
16753                          * @param {Roo.XComponent} the top level component.
16754                          */
16755                         'buildcomplete' : true
16756                         
16757                 }
16758 });
16759
16760 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16761  //
16762  /**
16763  * marked - a markdown parser
16764  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16765  * https://github.com/chjj/marked
16766  */
16767
16768
16769 /**
16770  *
16771  * Roo.Markdown - is a very crude wrapper around marked..
16772  *
16773  * usage:
16774  * 
16775  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16776  * 
16777  * Note: move the sample code to the bottom of this
16778  * file before uncommenting it.
16779  *
16780  */
16781
16782 Roo.Markdown = {};
16783 Roo.Markdown.toHtml = function(text) {
16784     
16785     var c = new Roo.Markdown.marked.setOptions({
16786             renderer: new Roo.Markdown.marked.Renderer(),
16787             gfm: true,
16788             tables: true,
16789             breaks: false,
16790             pedantic: false,
16791             sanitize: false,
16792             smartLists: true,
16793             smartypants: false
16794           });
16795     // A FEW HACKS!!?
16796     
16797     text = text.replace(/\\\n/g,' ');
16798     return Roo.Markdown.marked(text);
16799 };
16800 //
16801 // converter
16802 //
16803 // Wraps all "globals" so that the only thing
16804 // exposed is makeHtml().
16805 //
16806 (function() {
16807     
16808     /**
16809      * Block-Level Grammar
16810      */
16811     
16812     var block = {
16813       newline: /^\n+/,
16814       code: /^( {4}[^\n]+\n*)+/,
16815       fences: noop,
16816       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16817       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16818       nptable: noop,
16819       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16820       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16821       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16822       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16823       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16824       table: noop,
16825       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16826       text: /^[^\n]+/
16827     };
16828     
16829     block.bullet = /(?:[*+-]|\d+\.)/;
16830     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16831     block.item = replace(block.item, 'gm')
16832       (/bull/g, block.bullet)
16833       ();
16834     
16835     block.list = replace(block.list)
16836       (/bull/g, block.bullet)
16837       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16838       ('def', '\\n+(?=' + block.def.source + ')')
16839       ();
16840     
16841     block.blockquote = replace(block.blockquote)
16842       ('def', block.def)
16843       ();
16844     
16845     block._tag = '(?!(?:'
16846       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16847       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16848       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16849     
16850     block.html = replace(block.html)
16851       ('comment', /<!--[\s\S]*?-->/)
16852       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16853       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16854       (/tag/g, block._tag)
16855       ();
16856     
16857     block.paragraph = replace(block.paragraph)
16858       ('hr', block.hr)
16859       ('heading', block.heading)
16860       ('lheading', block.lheading)
16861       ('blockquote', block.blockquote)
16862       ('tag', '<' + block._tag)
16863       ('def', block.def)
16864       ();
16865     
16866     /**
16867      * Normal Block Grammar
16868      */
16869     
16870     block.normal = merge({}, block);
16871     
16872     /**
16873      * GFM Block Grammar
16874      */
16875     
16876     block.gfm = merge({}, block.normal, {
16877       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16878       paragraph: /^/,
16879       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16880     });
16881     
16882     block.gfm.paragraph = replace(block.paragraph)
16883       ('(?!', '(?!'
16884         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16885         + block.list.source.replace('\\1', '\\3') + '|')
16886       ();
16887     
16888     /**
16889      * GFM + Tables Block Grammar
16890      */
16891     
16892     block.tables = merge({}, block.gfm, {
16893       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16894       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16895     });
16896     
16897     /**
16898      * Block Lexer
16899      */
16900     
16901     function Lexer(options) {
16902       this.tokens = [];
16903       this.tokens.links = {};
16904       this.options = options || marked.defaults;
16905       this.rules = block.normal;
16906     
16907       if (this.options.gfm) {
16908         if (this.options.tables) {
16909           this.rules = block.tables;
16910         } else {
16911           this.rules = block.gfm;
16912         }
16913       }
16914     }
16915     
16916     /**
16917      * Expose Block Rules
16918      */
16919     
16920     Lexer.rules = block;
16921     
16922     /**
16923      * Static Lex Method
16924      */
16925     
16926     Lexer.lex = function(src, options) {
16927       var lexer = new Lexer(options);
16928       return lexer.lex(src);
16929     };
16930     
16931     /**
16932      * Preprocessing
16933      */
16934     
16935     Lexer.prototype.lex = function(src) {
16936       src = src
16937         .replace(/\r\n|\r/g, '\n')
16938         .replace(/\t/g, '    ')
16939         .replace(/\u00a0/g, ' ')
16940         .replace(/\u2424/g, '\n');
16941     
16942       return this.token(src, true);
16943     };
16944     
16945     /**
16946      * Lexing
16947      */
16948     
16949     Lexer.prototype.token = function(src, top, bq) {
16950       var src = src.replace(/^ +$/gm, '')
16951         , next
16952         , loose
16953         , cap
16954         , bull
16955         , b
16956         , item
16957         , space
16958         , i
16959         , l;
16960     
16961       while (src) {
16962         // newline
16963         if (cap = this.rules.newline.exec(src)) {
16964           src = src.substring(cap[0].length);
16965           if (cap[0].length > 1) {
16966             this.tokens.push({
16967               type: 'space'
16968             });
16969           }
16970         }
16971     
16972         // code
16973         if (cap = this.rules.code.exec(src)) {
16974           src = src.substring(cap[0].length);
16975           cap = cap[0].replace(/^ {4}/gm, '');
16976           this.tokens.push({
16977             type: 'code',
16978             text: !this.options.pedantic
16979               ? cap.replace(/\n+$/, '')
16980               : cap
16981           });
16982           continue;
16983         }
16984     
16985         // fences (gfm)
16986         if (cap = this.rules.fences.exec(src)) {
16987           src = src.substring(cap[0].length);
16988           this.tokens.push({
16989             type: 'code',
16990             lang: cap[2],
16991             text: cap[3] || ''
16992           });
16993           continue;
16994         }
16995     
16996         // heading
16997         if (cap = this.rules.heading.exec(src)) {
16998           src = src.substring(cap[0].length);
16999           this.tokens.push({
17000             type: 'heading',
17001             depth: cap[1].length,
17002             text: cap[2]
17003           });
17004           continue;
17005         }
17006     
17007         // table no leading pipe (gfm)
17008         if (top && (cap = this.rules.nptable.exec(src))) {
17009           src = src.substring(cap[0].length);
17010     
17011           item = {
17012             type: 'table',
17013             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17014             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17015             cells: cap[3].replace(/\n$/, '').split('\n')
17016           };
17017     
17018           for (i = 0; i < item.align.length; i++) {
17019             if (/^ *-+: *$/.test(item.align[i])) {
17020               item.align[i] = 'right';
17021             } else if (/^ *:-+: *$/.test(item.align[i])) {
17022               item.align[i] = 'center';
17023             } else if (/^ *:-+ *$/.test(item.align[i])) {
17024               item.align[i] = 'left';
17025             } else {
17026               item.align[i] = null;
17027             }
17028           }
17029     
17030           for (i = 0; i < item.cells.length; i++) {
17031             item.cells[i] = item.cells[i].split(/ *\| */);
17032           }
17033     
17034           this.tokens.push(item);
17035     
17036           continue;
17037         }
17038     
17039         // lheading
17040         if (cap = this.rules.lheading.exec(src)) {
17041           src = src.substring(cap[0].length);
17042           this.tokens.push({
17043             type: 'heading',
17044             depth: cap[2] === '=' ? 1 : 2,
17045             text: cap[1]
17046           });
17047           continue;
17048         }
17049     
17050         // hr
17051         if (cap = this.rules.hr.exec(src)) {
17052           src = src.substring(cap[0].length);
17053           this.tokens.push({
17054             type: 'hr'
17055           });
17056           continue;
17057         }
17058     
17059         // blockquote
17060         if (cap = this.rules.blockquote.exec(src)) {
17061           src = src.substring(cap[0].length);
17062     
17063           this.tokens.push({
17064             type: 'blockquote_start'
17065           });
17066     
17067           cap = cap[0].replace(/^ *> ?/gm, '');
17068     
17069           // Pass `top` to keep the current
17070           // "toplevel" state. This is exactly
17071           // how markdown.pl works.
17072           this.token(cap, top, true);
17073     
17074           this.tokens.push({
17075             type: 'blockquote_end'
17076           });
17077     
17078           continue;
17079         }
17080     
17081         // list
17082         if (cap = this.rules.list.exec(src)) {
17083           src = src.substring(cap[0].length);
17084           bull = cap[2];
17085     
17086           this.tokens.push({
17087             type: 'list_start',
17088             ordered: bull.length > 1
17089           });
17090     
17091           // Get each top-level item.
17092           cap = cap[0].match(this.rules.item);
17093     
17094           next = false;
17095           l = cap.length;
17096           i = 0;
17097     
17098           for (; i < l; i++) {
17099             item = cap[i];
17100     
17101             // Remove the list item's bullet
17102             // so it is seen as the next token.
17103             space = item.length;
17104             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17105     
17106             // Outdent whatever the
17107             // list item contains. Hacky.
17108             if (~item.indexOf('\n ')) {
17109               space -= item.length;
17110               item = !this.options.pedantic
17111                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17112                 : item.replace(/^ {1,4}/gm, '');
17113             }
17114     
17115             // Determine whether the next list item belongs here.
17116             // Backpedal if it does not belong in this list.
17117             if (this.options.smartLists && i !== l - 1) {
17118               b = block.bullet.exec(cap[i + 1])[0];
17119               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17120                 src = cap.slice(i + 1).join('\n') + src;
17121                 i = l - 1;
17122               }
17123             }
17124     
17125             // Determine whether item is loose or not.
17126             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17127             // for discount behavior.
17128             loose = next || /\n\n(?!\s*$)/.test(item);
17129             if (i !== l - 1) {
17130               next = item.charAt(item.length - 1) === '\n';
17131               if (!loose) { loose = next; }
17132             }
17133     
17134             this.tokens.push({
17135               type: loose
17136                 ? 'loose_item_start'
17137                 : 'list_item_start'
17138             });
17139     
17140             // Recurse.
17141             this.token(item, false, bq);
17142     
17143             this.tokens.push({
17144               type: 'list_item_end'
17145             });
17146           }
17147     
17148           this.tokens.push({
17149             type: 'list_end'
17150           });
17151     
17152           continue;
17153         }
17154     
17155         // html
17156         if (cap = this.rules.html.exec(src)) {
17157           src = src.substring(cap[0].length);
17158           this.tokens.push({
17159             type: this.options.sanitize
17160               ? 'paragraph'
17161               : 'html',
17162             pre: !this.options.sanitizer
17163               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17164             text: cap[0]
17165           });
17166           continue;
17167         }
17168     
17169         // def
17170         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17171           src = src.substring(cap[0].length);
17172           this.tokens.links[cap[1].toLowerCase()] = {
17173             href: cap[2],
17174             title: cap[3]
17175           };
17176           continue;
17177         }
17178     
17179         // table (gfm)
17180         if (top && (cap = this.rules.table.exec(src))) {
17181           src = src.substring(cap[0].length);
17182     
17183           item = {
17184             type: 'table',
17185             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17186             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17187             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17188           };
17189     
17190           for (i = 0; i < item.align.length; i++) {
17191             if (/^ *-+: *$/.test(item.align[i])) {
17192               item.align[i] = 'right';
17193             } else if (/^ *:-+: *$/.test(item.align[i])) {
17194               item.align[i] = 'center';
17195             } else if (/^ *:-+ *$/.test(item.align[i])) {
17196               item.align[i] = 'left';
17197             } else {
17198               item.align[i] = null;
17199             }
17200           }
17201     
17202           for (i = 0; i < item.cells.length; i++) {
17203             item.cells[i] = item.cells[i]
17204               .replace(/^ *\| *| *\| *$/g, '')
17205               .split(/ *\| */);
17206           }
17207     
17208           this.tokens.push(item);
17209     
17210           continue;
17211         }
17212     
17213         // top-level paragraph
17214         if (top && (cap = this.rules.paragraph.exec(src))) {
17215           src = src.substring(cap[0].length);
17216           this.tokens.push({
17217             type: 'paragraph',
17218             text: cap[1].charAt(cap[1].length - 1) === '\n'
17219               ? cap[1].slice(0, -1)
17220               : cap[1]
17221           });
17222           continue;
17223         }
17224     
17225         // text
17226         if (cap = this.rules.text.exec(src)) {
17227           // Top-level should never reach here.
17228           src = src.substring(cap[0].length);
17229           this.tokens.push({
17230             type: 'text',
17231             text: cap[0]
17232           });
17233           continue;
17234         }
17235     
17236         if (src) {
17237           throw new
17238             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17239         }
17240       }
17241     
17242       return this.tokens;
17243     };
17244     
17245     /**
17246      * Inline-Level Grammar
17247      */
17248     
17249     var inline = {
17250       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17251       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17252       url: noop,
17253       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17254       link: /^!?\[(inside)\]\(href\)/,
17255       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17256       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17257       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17258       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17259       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17260       br: /^ {2,}\n(?!\s*$)/,
17261       del: noop,
17262       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17263     };
17264     
17265     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17266     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17267     
17268     inline.link = replace(inline.link)
17269       ('inside', inline._inside)
17270       ('href', inline._href)
17271       ();
17272     
17273     inline.reflink = replace(inline.reflink)
17274       ('inside', inline._inside)
17275       ();
17276     
17277     /**
17278      * Normal Inline Grammar
17279      */
17280     
17281     inline.normal = merge({}, inline);
17282     
17283     /**
17284      * Pedantic Inline Grammar
17285      */
17286     
17287     inline.pedantic = merge({}, inline.normal, {
17288       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17289       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17290     });
17291     
17292     /**
17293      * GFM Inline Grammar
17294      */
17295     
17296     inline.gfm = merge({}, inline.normal, {
17297       escape: replace(inline.escape)('])', '~|])')(),
17298       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17299       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17300       text: replace(inline.text)
17301         (']|', '~]|')
17302         ('|', '|https?://|')
17303         ()
17304     });
17305     
17306     /**
17307      * GFM + Line Breaks Inline Grammar
17308      */
17309     
17310     inline.breaks = merge({}, inline.gfm, {
17311       br: replace(inline.br)('{2,}', '*')(),
17312       text: replace(inline.gfm.text)('{2,}', '*')()
17313     });
17314     
17315     /**
17316      * Inline Lexer & Compiler
17317      */
17318     
17319     function InlineLexer(links, options) {
17320       this.options = options || marked.defaults;
17321       this.links = links;
17322       this.rules = inline.normal;
17323       this.renderer = this.options.renderer || new Renderer;
17324       this.renderer.options = this.options;
17325     
17326       if (!this.links) {
17327         throw new
17328           Error('Tokens array requires a `links` property.');
17329       }
17330     
17331       if (this.options.gfm) {
17332         if (this.options.breaks) {
17333           this.rules = inline.breaks;
17334         } else {
17335           this.rules = inline.gfm;
17336         }
17337       } else if (this.options.pedantic) {
17338         this.rules = inline.pedantic;
17339       }
17340     }
17341     
17342     /**
17343      * Expose Inline Rules
17344      */
17345     
17346     InlineLexer.rules = inline;
17347     
17348     /**
17349      * Static Lexing/Compiling Method
17350      */
17351     
17352     InlineLexer.output = function(src, links, options) {
17353       var inline = new InlineLexer(links, options);
17354       return inline.output(src);
17355     };
17356     
17357     /**
17358      * Lexing/Compiling
17359      */
17360     
17361     InlineLexer.prototype.output = function(src) {
17362       var out = ''
17363         , link
17364         , text
17365         , href
17366         , cap;
17367     
17368       while (src) {
17369         // escape
17370         if (cap = this.rules.escape.exec(src)) {
17371           src = src.substring(cap[0].length);
17372           out += cap[1];
17373           continue;
17374         }
17375     
17376         // autolink
17377         if (cap = this.rules.autolink.exec(src)) {
17378           src = src.substring(cap[0].length);
17379           if (cap[2] === '@') {
17380             text = cap[1].charAt(6) === ':'
17381               ? this.mangle(cap[1].substring(7))
17382               : this.mangle(cap[1]);
17383             href = this.mangle('mailto:') + text;
17384           } else {
17385             text = escape(cap[1]);
17386             href = text;
17387           }
17388           out += this.renderer.link(href, null, text);
17389           continue;
17390         }
17391     
17392         // url (gfm)
17393         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17394           src = src.substring(cap[0].length);
17395           text = escape(cap[1]);
17396           href = text;
17397           out += this.renderer.link(href, null, text);
17398           continue;
17399         }
17400     
17401         // tag
17402         if (cap = this.rules.tag.exec(src)) {
17403           if (!this.inLink && /^<a /i.test(cap[0])) {
17404             this.inLink = true;
17405           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17406             this.inLink = false;
17407           }
17408           src = src.substring(cap[0].length);
17409           out += this.options.sanitize
17410             ? this.options.sanitizer
17411               ? this.options.sanitizer(cap[0])
17412               : escape(cap[0])
17413             : cap[0];
17414           continue;
17415         }
17416     
17417         // link
17418         if (cap = this.rules.link.exec(src)) {
17419           src = src.substring(cap[0].length);
17420           this.inLink = true;
17421           out += this.outputLink(cap, {
17422             href: cap[2],
17423             title: cap[3]
17424           });
17425           this.inLink = false;
17426           continue;
17427         }
17428     
17429         // reflink, nolink
17430         if ((cap = this.rules.reflink.exec(src))
17431             || (cap = this.rules.nolink.exec(src))) {
17432           src = src.substring(cap[0].length);
17433           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17434           link = this.links[link.toLowerCase()];
17435           if (!link || !link.href) {
17436             out += cap[0].charAt(0);
17437             src = cap[0].substring(1) + src;
17438             continue;
17439           }
17440           this.inLink = true;
17441           out += this.outputLink(cap, link);
17442           this.inLink = false;
17443           continue;
17444         }
17445     
17446         // strong
17447         if (cap = this.rules.strong.exec(src)) {
17448           src = src.substring(cap[0].length);
17449           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17450           continue;
17451         }
17452     
17453         // em
17454         if (cap = this.rules.em.exec(src)) {
17455           src = src.substring(cap[0].length);
17456           out += this.renderer.em(this.output(cap[2] || cap[1]));
17457           continue;
17458         }
17459     
17460         // code
17461         if (cap = this.rules.code.exec(src)) {
17462           src = src.substring(cap[0].length);
17463           out += this.renderer.codespan(escape(cap[2], true));
17464           continue;
17465         }
17466     
17467         // br
17468         if (cap = this.rules.br.exec(src)) {
17469           src = src.substring(cap[0].length);
17470           out += this.renderer.br();
17471           continue;
17472         }
17473     
17474         // del (gfm)
17475         if (cap = this.rules.del.exec(src)) {
17476           src = src.substring(cap[0].length);
17477           out += this.renderer.del(this.output(cap[1]));
17478           continue;
17479         }
17480     
17481         // text
17482         if (cap = this.rules.text.exec(src)) {
17483           src = src.substring(cap[0].length);
17484           out += this.renderer.text(escape(this.smartypants(cap[0])));
17485           continue;
17486         }
17487     
17488         if (src) {
17489           throw new
17490             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17491         }
17492       }
17493     
17494       return out;
17495     };
17496     
17497     /**
17498      * Compile Link
17499      */
17500     
17501     InlineLexer.prototype.outputLink = function(cap, link) {
17502       var href = escape(link.href)
17503         , title = link.title ? escape(link.title) : null;
17504     
17505       return cap[0].charAt(0) !== '!'
17506         ? this.renderer.link(href, title, this.output(cap[1]))
17507         : this.renderer.image(href, title, escape(cap[1]));
17508     };
17509     
17510     /**
17511      * Smartypants Transformations
17512      */
17513     
17514     InlineLexer.prototype.smartypants = function(text) {
17515       if (!this.options.smartypants)  { return text; }
17516       return text
17517         // em-dashes
17518         .replace(/---/g, '\u2014')
17519         // en-dashes
17520         .replace(/--/g, '\u2013')
17521         // opening singles
17522         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17523         // closing singles & apostrophes
17524         .replace(/'/g, '\u2019')
17525         // opening doubles
17526         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17527         // closing doubles
17528         .replace(/"/g, '\u201d')
17529         // ellipses
17530         .replace(/\.{3}/g, '\u2026');
17531     };
17532     
17533     /**
17534      * Mangle Links
17535      */
17536     
17537     InlineLexer.prototype.mangle = function(text) {
17538       if (!this.options.mangle) { return text; }
17539       var out = ''
17540         , l = text.length
17541         , i = 0
17542         , ch;
17543     
17544       for (; i < l; i++) {
17545         ch = text.charCodeAt(i);
17546         if (Math.random() > 0.5) {
17547           ch = 'x' + ch.toString(16);
17548         }
17549         out += '&#' + ch + ';';
17550       }
17551     
17552       return out;
17553     };
17554     
17555     /**
17556      * Renderer
17557      */
17558     
17559     function Renderer(options) {
17560       this.options = options || {};
17561     }
17562     
17563     Renderer.prototype.code = function(code, lang, escaped) {
17564       if (this.options.highlight) {
17565         var out = this.options.highlight(code, lang);
17566         if (out != null && out !== code) {
17567           escaped = true;
17568           code = out;
17569         }
17570       } else {
17571             // hack!!! - it's already escapeD?
17572             escaped = true;
17573       }
17574     
17575       if (!lang) {
17576         return '<pre><code>'
17577           + (escaped ? code : escape(code, true))
17578           + '\n</code></pre>';
17579       }
17580     
17581       return '<pre><code class="'
17582         + this.options.langPrefix
17583         + escape(lang, true)
17584         + '">'
17585         + (escaped ? code : escape(code, true))
17586         + '\n</code></pre>\n';
17587     };
17588     
17589     Renderer.prototype.blockquote = function(quote) {
17590       return '<blockquote>\n' + quote + '</blockquote>\n';
17591     };
17592     
17593     Renderer.prototype.html = function(html) {
17594       return html;
17595     };
17596     
17597     Renderer.prototype.heading = function(text, level, raw) {
17598       return '<h'
17599         + level
17600         + ' id="'
17601         + this.options.headerPrefix
17602         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17603         + '">'
17604         + text
17605         + '</h'
17606         + level
17607         + '>\n';
17608     };
17609     
17610     Renderer.prototype.hr = function() {
17611       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17612     };
17613     
17614     Renderer.prototype.list = function(body, ordered) {
17615       var type = ordered ? 'ol' : 'ul';
17616       return '<' + type + '>\n' + body + '</' + type + '>\n';
17617     };
17618     
17619     Renderer.prototype.listitem = function(text) {
17620       return '<li>' + text + '</li>\n';
17621     };
17622     
17623     Renderer.prototype.paragraph = function(text) {
17624       return '<p>' + text + '</p>\n';
17625     };
17626     
17627     Renderer.prototype.table = function(header, body) {
17628       return '<table class="table table-striped">\n'
17629         + '<thead>\n'
17630         + header
17631         + '</thead>\n'
17632         + '<tbody>\n'
17633         + body
17634         + '</tbody>\n'
17635         + '</table>\n';
17636     };
17637     
17638     Renderer.prototype.tablerow = function(content) {
17639       return '<tr>\n' + content + '</tr>\n';
17640     };
17641     
17642     Renderer.prototype.tablecell = function(content, flags) {
17643       var type = flags.header ? 'th' : 'td';
17644       var tag = flags.align
17645         ? '<' + type + ' style="text-align:' + flags.align + '">'
17646         : '<' + type + '>';
17647       return tag + content + '</' + type + '>\n';
17648     };
17649     
17650     // span level renderer
17651     Renderer.prototype.strong = function(text) {
17652       return '<strong>' + text + '</strong>';
17653     };
17654     
17655     Renderer.prototype.em = function(text) {
17656       return '<em>' + text + '</em>';
17657     };
17658     
17659     Renderer.prototype.codespan = function(text) {
17660       return '<code>' + text + '</code>';
17661     };
17662     
17663     Renderer.prototype.br = function() {
17664       return this.options.xhtml ? '<br/>' : '<br>';
17665     };
17666     
17667     Renderer.prototype.del = function(text) {
17668       return '<del>' + text + '</del>';
17669     };
17670     
17671     Renderer.prototype.link = function(href, title, text) {
17672       if (this.options.sanitize) {
17673         try {
17674           var prot = decodeURIComponent(unescape(href))
17675             .replace(/[^\w:]/g, '')
17676             .toLowerCase();
17677         } catch (e) {
17678           return '';
17679         }
17680         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17681           return '';
17682         }
17683       }
17684       var out = '<a href="' + href + '"';
17685       if (title) {
17686         out += ' title="' + title + '"';
17687       }
17688       out += '>' + text + '</a>';
17689       return out;
17690     };
17691     
17692     Renderer.prototype.image = function(href, title, text) {
17693       var out = '<img src="' + href + '" alt="' + text + '"';
17694       if (title) {
17695         out += ' title="' + title + '"';
17696       }
17697       out += this.options.xhtml ? '/>' : '>';
17698       return out;
17699     };
17700     
17701     Renderer.prototype.text = function(text) {
17702       return text;
17703     };
17704     
17705     /**
17706      * Parsing & Compiling
17707      */
17708     
17709     function Parser(options) {
17710       this.tokens = [];
17711       this.token = null;
17712       this.options = options || marked.defaults;
17713       this.options.renderer = this.options.renderer || new Renderer;
17714       this.renderer = this.options.renderer;
17715       this.renderer.options = this.options;
17716     }
17717     
17718     /**
17719      * Static Parse Method
17720      */
17721     
17722     Parser.parse = function(src, options, renderer) {
17723       var parser = new Parser(options, renderer);
17724       return parser.parse(src);
17725     };
17726     
17727     /**
17728      * Parse Loop
17729      */
17730     
17731     Parser.prototype.parse = function(src) {
17732       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17733       this.tokens = src.reverse();
17734     
17735       var out = '';
17736       while (this.next()) {
17737         out += this.tok();
17738       }
17739     
17740       return out;
17741     };
17742     
17743     /**
17744      * Next Token
17745      */
17746     
17747     Parser.prototype.next = function() {
17748       return this.token = this.tokens.pop();
17749     };
17750     
17751     /**
17752      * Preview Next Token
17753      */
17754     
17755     Parser.prototype.peek = function() {
17756       return this.tokens[this.tokens.length - 1] || 0;
17757     };
17758     
17759     /**
17760      * Parse Text Tokens
17761      */
17762     
17763     Parser.prototype.parseText = function() {
17764       var body = this.token.text;
17765     
17766       while (this.peek().type === 'text') {
17767         body += '\n' + this.next().text;
17768       }
17769     
17770       return this.inline.output(body);
17771     };
17772     
17773     /**
17774      * Parse Current Token
17775      */
17776     
17777     Parser.prototype.tok = function() {
17778       switch (this.token.type) {
17779         case 'space': {
17780           return '';
17781         }
17782         case 'hr': {
17783           return this.renderer.hr();
17784         }
17785         case 'heading': {
17786           return this.renderer.heading(
17787             this.inline.output(this.token.text),
17788             this.token.depth,
17789             this.token.text);
17790         }
17791         case 'code': {
17792           return this.renderer.code(this.token.text,
17793             this.token.lang,
17794             this.token.escaped);
17795         }
17796         case 'table': {
17797           var header = ''
17798             , body = ''
17799             , i
17800             , row
17801             , cell
17802             , flags
17803             , j;
17804     
17805           // header
17806           cell = '';
17807           for (i = 0; i < this.token.header.length; i++) {
17808             flags = { header: true, align: this.token.align[i] };
17809             cell += this.renderer.tablecell(
17810               this.inline.output(this.token.header[i]),
17811               { header: true, align: this.token.align[i] }
17812             );
17813           }
17814           header += this.renderer.tablerow(cell);
17815     
17816           for (i = 0; i < this.token.cells.length; i++) {
17817             row = this.token.cells[i];
17818     
17819             cell = '';
17820             for (j = 0; j < row.length; j++) {
17821               cell += this.renderer.tablecell(
17822                 this.inline.output(row[j]),
17823                 { header: false, align: this.token.align[j] }
17824               );
17825             }
17826     
17827             body += this.renderer.tablerow(cell);
17828           }
17829           return this.renderer.table(header, body);
17830         }
17831         case 'blockquote_start': {
17832           var body = '';
17833     
17834           while (this.next().type !== 'blockquote_end') {
17835             body += this.tok();
17836           }
17837     
17838           return this.renderer.blockquote(body);
17839         }
17840         case 'list_start': {
17841           var body = ''
17842             , ordered = this.token.ordered;
17843     
17844           while (this.next().type !== 'list_end') {
17845             body += this.tok();
17846           }
17847     
17848           return this.renderer.list(body, ordered);
17849         }
17850         case 'list_item_start': {
17851           var body = '';
17852     
17853           while (this.next().type !== 'list_item_end') {
17854             body += this.token.type === 'text'
17855               ? this.parseText()
17856               : this.tok();
17857           }
17858     
17859           return this.renderer.listitem(body);
17860         }
17861         case 'loose_item_start': {
17862           var body = '';
17863     
17864           while (this.next().type !== 'list_item_end') {
17865             body += this.tok();
17866           }
17867     
17868           return this.renderer.listitem(body);
17869         }
17870         case 'html': {
17871           var html = !this.token.pre && !this.options.pedantic
17872             ? this.inline.output(this.token.text)
17873             : this.token.text;
17874           return this.renderer.html(html);
17875         }
17876         case 'paragraph': {
17877           return this.renderer.paragraph(this.inline.output(this.token.text));
17878         }
17879         case 'text': {
17880           return this.renderer.paragraph(this.parseText());
17881         }
17882       }
17883     };
17884     
17885     /**
17886      * Helpers
17887      */
17888     
17889     function escape(html, encode) {
17890       return html
17891         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17892         .replace(/</g, '&lt;')
17893         .replace(/>/g, '&gt;')
17894         .replace(/"/g, '&quot;')
17895         .replace(/'/g, '&#39;');
17896     }
17897     
17898     function unescape(html) {
17899         // explicitly match decimal, hex, and named HTML entities 
17900       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17901         n = n.toLowerCase();
17902         if (n === 'colon') { return ':'; }
17903         if (n.charAt(0) === '#') {
17904           return n.charAt(1) === 'x'
17905             ? String.fromCharCode(parseInt(n.substring(2), 16))
17906             : String.fromCharCode(+n.substring(1));
17907         }
17908         return '';
17909       });
17910     }
17911     
17912     function replace(regex, opt) {
17913       regex = regex.source;
17914       opt = opt || '';
17915       return function self(name, val) {
17916         if (!name) { return new RegExp(regex, opt); }
17917         val = val.source || val;
17918         val = val.replace(/(^|[^\[])\^/g, '$1');
17919         regex = regex.replace(name, val);
17920         return self;
17921       };
17922     }
17923     
17924     function noop() {}
17925     noop.exec = noop;
17926     
17927     function merge(obj) {
17928       var i = 1
17929         , target
17930         , key;
17931     
17932       for (; i < arguments.length; i++) {
17933         target = arguments[i];
17934         for (key in target) {
17935           if (Object.prototype.hasOwnProperty.call(target, key)) {
17936             obj[key] = target[key];
17937           }
17938         }
17939       }
17940     
17941       return obj;
17942     }
17943     
17944     
17945     /**
17946      * Marked
17947      */
17948     
17949     function marked(src, opt, callback) {
17950       if (callback || typeof opt === 'function') {
17951         if (!callback) {
17952           callback = opt;
17953           opt = null;
17954         }
17955     
17956         opt = merge({}, marked.defaults, opt || {});
17957     
17958         var highlight = opt.highlight
17959           , tokens
17960           , pending
17961           , i = 0;
17962     
17963         try {
17964           tokens = Lexer.lex(src, opt)
17965         } catch (e) {
17966           return callback(e);
17967         }
17968     
17969         pending = tokens.length;
17970     
17971         var done = function(err) {
17972           if (err) {
17973             opt.highlight = highlight;
17974             return callback(err);
17975           }
17976     
17977           var out;
17978     
17979           try {
17980             out = Parser.parse(tokens, opt);
17981           } catch (e) {
17982             err = e;
17983           }
17984     
17985           opt.highlight = highlight;
17986     
17987           return err
17988             ? callback(err)
17989             : callback(null, out);
17990         };
17991     
17992         if (!highlight || highlight.length < 3) {
17993           return done();
17994         }
17995     
17996         delete opt.highlight;
17997     
17998         if (!pending) { return done(); }
17999     
18000         for (; i < tokens.length; i++) {
18001           (function(token) {
18002             if (token.type !== 'code') {
18003               return --pending || done();
18004             }
18005             return highlight(token.text, token.lang, function(err, code) {
18006               if (err) { return done(err); }
18007               if (code == null || code === token.text) {
18008                 return --pending || done();
18009               }
18010               token.text = code;
18011               token.escaped = true;
18012               --pending || done();
18013             });
18014           })(tokens[i]);
18015         }
18016     
18017         return;
18018       }
18019       try {
18020         if (opt) { opt = merge({}, marked.defaults, opt); }
18021         return Parser.parse(Lexer.lex(src, opt), opt);
18022       } catch (e) {
18023         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18024         if ((opt || marked.defaults).silent) {
18025           return '<p>An error occured:</p><pre>'
18026             + escape(e.message + '', true)
18027             + '</pre>';
18028         }
18029         throw e;
18030       }
18031     }
18032     
18033     /**
18034      * Options
18035      */
18036     
18037     marked.options =
18038     marked.setOptions = function(opt) {
18039       merge(marked.defaults, opt);
18040       return marked;
18041     };
18042     
18043     marked.defaults = {
18044       gfm: true,
18045       tables: true,
18046       breaks: false,
18047       pedantic: false,
18048       sanitize: false,
18049       sanitizer: null,
18050       mangle: true,
18051       smartLists: false,
18052       silent: false,
18053       highlight: null,
18054       langPrefix: 'lang-',
18055       smartypants: false,
18056       headerPrefix: '',
18057       renderer: new Renderer,
18058       xhtml: false
18059     };
18060     
18061     /**
18062      * Expose
18063      */
18064     
18065     marked.Parser = Parser;
18066     marked.parser = Parser.parse;
18067     
18068     marked.Renderer = Renderer;
18069     
18070     marked.Lexer = Lexer;
18071     marked.lexer = Lexer.lex;
18072     
18073     marked.InlineLexer = InlineLexer;
18074     marked.inlineLexer = InlineLexer.output;
18075     
18076     marked.parse = marked;
18077     
18078     Roo.Markdown.marked = marked;
18079
18080 })();/*
18081  * Based on:
18082  * Ext JS Library 1.1.1
18083  * Copyright(c) 2006-2007, Ext JS, LLC.
18084  *
18085  * Originally Released Under LGPL - original licence link has changed is not relivant.
18086  *
18087  * Fork - LGPL
18088  * <script type="text/javascript">
18089  */
18090
18091
18092
18093 /*
18094  * These classes are derivatives of the similarly named classes in the YUI Library.
18095  * The original license:
18096  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18097  * Code licensed under the BSD License:
18098  * http://developer.yahoo.net/yui/license.txt
18099  */
18100
18101 (function() {
18102
18103 var Event=Roo.EventManager;
18104 var Dom=Roo.lib.Dom;
18105
18106 /**
18107  * @class Roo.dd.DragDrop
18108  * @extends Roo.util.Observable
18109  * Defines the interface and base operation of items that that can be
18110  * dragged or can be drop targets.  It was designed to be extended, overriding
18111  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18112  * Up to three html elements can be associated with a DragDrop instance:
18113  * <ul>
18114  * <li>linked element: the element that is passed into the constructor.
18115  * This is the element which defines the boundaries for interaction with
18116  * other DragDrop objects.</li>
18117  * <li>handle element(s): The drag operation only occurs if the element that
18118  * was clicked matches a handle element.  By default this is the linked
18119  * element, but there are times that you will want only a portion of the
18120  * linked element to initiate the drag operation, and the setHandleElId()
18121  * method provides a way to define this.</li>
18122  * <li>drag element: this represents the element that would be moved along
18123  * with the cursor during a drag operation.  By default, this is the linked
18124  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18125  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18126  * </li>
18127  * </ul>
18128  * This class should not be instantiated until the onload event to ensure that
18129  * the associated elements are available.
18130  * The following would define a DragDrop obj that would interact with any
18131  * other DragDrop obj in the "group1" group:
18132  * <pre>
18133  *  dd = new Roo.dd.DragDrop("div1", "group1");
18134  * </pre>
18135  * Since none of the event handlers have been implemented, nothing would
18136  * actually happen if you were to run the code above.  Normally you would
18137  * override this class or one of the default implementations, but you can
18138  * also override the methods you want on an instance of the class...
18139  * <pre>
18140  *  dd.onDragDrop = function(e, id) {
18141  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18142  *  }
18143  * </pre>
18144  * @constructor
18145  * @param {String} id of the element that is linked to this instance
18146  * @param {String} sGroup the group of related DragDrop objects
18147  * @param {object} config an object containing configurable attributes
18148  *                Valid properties for DragDrop:
18149  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18150  */
18151 Roo.dd.DragDrop = function(id, sGroup, config) {
18152     if (id) {
18153         this.init(id, sGroup, config);
18154     }
18155     
18156 };
18157
18158 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18159
18160     /**
18161      * The id of the element associated with this object.  This is what we
18162      * refer to as the "linked element" because the size and position of
18163      * this element is used to determine when the drag and drop objects have
18164      * interacted.
18165      * @property id
18166      * @type String
18167      */
18168     id: null,
18169
18170     /**
18171      * Configuration attributes passed into the constructor
18172      * @property config
18173      * @type object
18174      */
18175     config: null,
18176
18177     /**
18178      * The id of the element that will be dragged.  By default this is same
18179      * as the linked element , but could be changed to another element. Ex:
18180      * Roo.dd.DDProxy
18181      * @property dragElId
18182      * @type String
18183      * @private
18184      */
18185     dragElId: null,
18186
18187     /**
18188      * the id of the element that initiates the drag operation.  By default
18189      * this is the linked element, but could be changed to be a child of this
18190      * element.  This lets us do things like only starting the drag when the
18191      * header element within the linked html element is clicked.
18192      * @property handleElId
18193      * @type String
18194      * @private
18195      */
18196     handleElId: null,
18197
18198     /**
18199      * An associative array of HTML tags that will be ignored if clicked.
18200      * @property invalidHandleTypes
18201      * @type {string: string}
18202      */
18203     invalidHandleTypes: null,
18204
18205     /**
18206      * An associative array of ids for elements that will be ignored if clicked
18207      * @property invalidHandleIds
18208      * @type {string: string}
18209      */
18210     invalidHandleIds: null,
18211
18212     /**
18213      * An indexted array of css class names for elements that will be ignored
18214      * if clicked.
18215      * @property invalidHandleClasses
18216      * @type string[]
18217      */
18218     invalidHandleClasses: null,
18219
18220     /**
18221      * The linked element's absolute X position at the time the drag was
18222      * started
18223      * @property startPageX
18224      * @type int
18225      * @private
18226      */
18227     startPageX: 0,
18228
18229     /**
18230      * The linked element's absolute X position at the time the drag was
18231      * started
18232      * @property startPageY
18233      * @type int
18234      * @private
18235      */
18236     startPageY: 0,
18237
18238     /**
18239      * The group defines a logical collection of DragDrop objects that are
18240      * related.  Instances only get events when interacting with other
18241      * DragDrop object in the same group.  This lets us define multiple
18242      * groups using a single DragDrop subclass if we want.
18243      * @property groups
18244      * @type {string: string}
18245      */
18246     groups: null,
18247
18248     /**
18249      * Individual drag/drop instances can be locked.  This will prevent
18250      * onmousedown start drag.
18251      * @property locked
18252      * @type boolean
18253      * @private
18254      */
18255     locked: false,
18256
18257     /**
18258      * Lock this instance
18259      * @method lock
18260      */
18261     lock: function() { this.locked = true; },
18262
18263     /**
18264      * Unlock this instace
18265      * @method unlock
18266      */
18267     unlock: function() { this.locked = false; },
18268
18269     /**
18270      * By default, all insances can be a drop target.  This can be disabled by
18271      * setting isTarget to false.
18272      * @method isTarget
18273      * @type boolean
18274      */
18275     isTarget: true,
18276
18277     /**
18278      * The padding configured for this drag and drop object for calculating
18279      * the drop zone intersection with this object.
18280      * @method padding
18281      * @type int[]
18282      */
18283     padding: null,
18284
18285     /**
18286      * Cached reference to the linked element
18287      * @property _domRef
18288      * @private
18289      */
18290     _domRef: null,
18291
18292     /**
18293      * Internal typeof flag
18294      * @property __ygDragDrop
18295      * @private
18296      */
18297     __ygDragDrop: true,
18298
18299     /**
18300      * Set to true when horizontal contraints are applied
18301      * @property constrainX
18302      * @type boolean
18303      * @private
18304      */
18305     constrainX: false,
18306
18307     /**
18308      * Set to true when vertical contraints are applied
18309      * @property constrainY
18310      * @type boolean
18311      * @private
18312      */
18313     constrainY: false,
18314
18315     /**
18316      * The left constraint
18317      * @property minX
18318      * @type int
18319      * @private
18320      */
18321     minX: 0,
18322
18323     /**
18324      * The right constraint
18325      * @property maxX
18326      * @type int
18327      * @private
18328      */
18329     maxX: 0,
18330
18331     /**
18332      * The up constraint
18333      * @property minY
18334      * @type int
18335      * @type int
18336      * @private
18337      */
18338     minY: 0,
18339
18340     /**
18341      * The down constraint
18342      * @property maxY
18343      * @type int
18344      * @private
18345      */
18346     maxY: 0,
18347
18348     /**
18349      * Maintain offsets when we resetconstraints.  Set to true when you want
18350      * the position of the element relative to its parent to stay the same
18351      * when the page changes
18352      *
18353      * @property maintainOffset
18354      * @type boolean
18355      */
18356     maintainOffset: false,
18357
18358     /**
18359      * Array of pixel locations the element will snap to if we specified a
18360      * horizontal graduation/interval.  This array is generated automatically
18361      * when you define a tick interval.
18362      * @property xTicks
18363      * @type int[]
18364      */
18365     xTicks: null,
18366
18367     /**
18368      * Array of pixel locations the element will snap to if we specified a
18369      * vertical graduation/interval.  This array is generated automatically
18370      * when you define a tick interval.
18371      * @property yTicks
18372      * @type int[]
18373      */
18374     yTicks: null,
18375
18376     /**
18377      * By default the drag and drop instance will only respond to the primary
18378      * button click (left button for a right-handed mouse).  Set to true to
18379      * allow drag and drop to start with any mouse click that is propogated
18380      * by the browser
18381      * @property primaryButtonOnly
18382      * @type boolean
18383      */
18384     primaryButtonOnly: true,
18385
18386     /**
18387      * The availabe property is false until the linked dom element is accessible.
18388      * @property available
18389      * @type boolean
18390      */
18391     available: false,
18392
18393     /**
18394      * By default, drags can only be initiated if the mousedown occurs in the
18395      * region the linked element is.  This is done in part to work around a
18396      * bug in some browsers that mis-report the mousedown if the previous
18397      * mouseup happened outside of the window.  This property is set to true
18398      * if outer handles are defined.
18399      *
18400      * @property hasOuterHandles
18401      * @type boolean
18402      * @default false
18403      */
18404     hasOuterHandles: false,
18405
18406     /**
18407      * Code that executes immediately before the startDrag event
18408      * @method b4StartDrag
18409      * @private
18410      */
18411     b4StartDrag: function(x, y) { },
18412
18413     /**
18414      * Abstract method called after a drag/drop object is clicked
18415      * and the drag or mousedown time thresholds have beeen met.
18416      * @method startDrag
18417      * @param {int} X click location
18418      * @param {int} Y click location
18419      */
18420     startDrag: function(x, y) { /* override this */ },
18421
18422     /**
18423      * Code that executes immediately before the onDrag event
18424      * @method b4Drag
18425      * @private
18426      */
18427     b4Drag: function(e) { },
18428
18429     /**
18430      * Abstract method called during the onMouseMove event while dragging an
18431      * object.
18432      * @method onDrag
18433      * @param {Event} e the mousemove event
18434      */
18435     onDrag: function(e) { /* override this */ },
18436
18437     /**
18438      * Abstract method called when this element fist begins hovering over
18439      * another DragDrop obj
18440      * @method onDragEnter
18441      * @param {Event} e the mousemove event
18442      * @param {String|DragDrop[]} id In POINT mode, the element
18443      * id this is hovering over.  In INTERSECT mode, an array of one or more
18444      * dragdrop items being hovered over.
18445      */
18446     onDragEnter: function(e, id) { /* override this */ },
18447
18448     /**
18449      * Code that executes immediately before the onDragOver event
18450      * @method b4DragOver
18451      * @private
18452      */
18453     b4DragOver: function(e) { },
18454
18455     /**
18456      * Abstract method called when this element is hovering over another
18457      * DragDrop obj
18458      * @method onDragOver
18459      * @param {Event} e the mousemove event
18460      * @param {String|DragDrop[]} id In POINT mode, the element
18461      * id this is hovering over.  In INTERSECT mode, an array of dd items
18462      * being hovered over.
18463      */
18464     onDragOver: function(e, id) { /* override this */ },
18465
18466     /**
18467      * Code that executes immediately before the onDragOut event
18468      * @method b4DragOut
18469      * @private
18470      */
18471     b4DragOut: function(e) { },
18472
18473     /**
18474      * Abstract method called when we are no longer hovering over an element
18475      * @method onDragOut
18476      * @param {Event} e the mousemove event
18477      * @param {String|DragDrop[]} id In POINT mode, the element
18478      * id this was hovering over.  In INTERSECT mode, an array of dd items
18479      * that the mouse is no longer over.
18480      */
18481     onDragOut: function(e, id) { /* override this */ },
18482
18483     /**
18484      * Code that executes immediately before the onDragDrop event
18485      * @method b4DragDrop
18486      * @private
18487      */
18488     b4DragDrop: function(e) { },
18489
18490     /**
18491      * Abstract method called when this item is dropped on another DragDrop
18492      * obj
18493      * @method onDragDrop
18494      * @param {Event} e the mouseup event
18495      * @param {String|DragDrop[]} id In POINT mode, the element
18496      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18497      * was dropped on.
18498      */
18499     onDragDrop: function(e, id) { /* override this */ },
18500
18501     /**
18502      * Abstract method called when this item is dropped on an area with no
18503      * drop target
18504      * @method onInvalidDrop
18505      * @param {Event} e the mouseup event
18506      */
18507     onInvalidDrop: function(e) { /* override this */ },
18508
18509     /**
18510      * Code that executes immediately before the endDrag event
18511      * @method b4EndDrag
18512      * @private
18513      */
18514     b4EndDrag: function(e) { },
18515
18516     /**
18517      * Fired when we are done dragging the object
18518      * @method endDrag
18519      * @param {Event} e the mouseup event
18520      */
18521     endDrag: function(e) { /* override this */ },
18522
18523     /**
18524      * Code executed immediately before the onMouseDown event
18525      * @method b4MouseDown
18526      * @param {Event} e the mousedown event
18527      * @private
18528      */
18529     b4MouseDown: function(e) {  },
18530
18531     /**
18532      * Event handler that fires when a drag/drop obj gets a mousedown
18533      * @method onMouseDown
18534      * @param {Event} e the mousedown event
18535      */
18536     onMouseDown: function(e) { /* override this */ },
18537
18538     /**
18539      * Event handler that fires when a drag/drop obj gets a mouseup
18540      * @method onMouseUp
18541      * @param {Event} e the mouseup event
18542      */
18543     onMouseUp: function(e) { /* override this */ },
18544
18545     /**
18546      * Override the onAvailable method to do what is needed after the initial
18547      * position was determined.
18548      * @method onAvailable
18549      */
18550     onAvailable: function () {
18551     },
18552
18553     /*
18554      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18555      * @type Object
18556      */
18557     defaultPadding : {left:0, right:0, top:0, bottom:0},
18558
18559     /*
18560      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18561  *
18562  * Usage:
18563  <pre><code>
18564  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18565                 { dragElId: "existingProxyDiv" });
18566  dd.startDrag = function(){
18567      this.constrainTo("parent-id");
18568  };
18569  </code></pre>
18570  * Or you can initalize it using the {@link Roo.Element} object:
18571  <pre><code>
18572  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18573      startDrag : function(){
18574          this.constrainTo("parent-id");
18575      }
18576  });
18577  </code></pre>
18578      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18579      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18580      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18581      * an object containing the sides to pad. For example: {right:10, bottom:10}
18582      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18583      */
18584     constrainTo : function(constrainTo, pad, inContent){
18585         if(typeof pad == "number"){
18586             pad = {left: pad, right:pad, top:pad, bottom:pad};
18587         }
18588         pad = pad || this.defaultPadding;
18589         var b = Roo.get(this.getEl()).getBox();
18590         var ce = Roo.get(constrainTo);
18591         var s = ce.getScroll();
18592         var c, cd = ce.dom;
18593         if(cd == document.body){
18594             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18595         }else{
18596             xy = ce.getXY();
18597             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18598         }
18599
18600
18601         var topSpace = b.y - c.y;
18602         var leftSpace = b.x - c.x;
18603
18604         this.resetConstraints();
18605         this.setXConstraint(leftSpace - (pad.left||0), // left
18606                 c.width - leftSpace - b.width - (pad.right||0) //right
18607         );
18608         this.setYConstraint(topSpace - (pad.top||0), //top
18609                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18610         );
18611     },
18612
18613     /**
18614      * Returns a reference to the linked element
18615      * @method getEl
18616      * @return {HTMLElement} the html element
18617      */
18618     getEl: function() {
18619         if (!this._domRef) {
18620             this._domRef = Roo.getDom(this.id);
18621         }
18622
18623         return this._domRef;
18624     },
18625
18626     /**
18627      * Returns a reference to the actual element to drag.  By default this is
18628      * the same as the html element, but it can be assigned to another
18629      * element. An example of this can be found in Roo.dd.DDProxy
18630      * @method getDragEl
18631      * @return {HTMLElement} the html element
18632      */
18633     getDragEl: function() {
18634         return Roo.getDom(this.dragElId);
18635     },
18636
18637     /**
18638      * Sets up the DragDrop object.  Must be called in the constructor of any
18639      * Roo.dd.DragDrop subclass
18640      * @method init
18641      * @param id the id of the linked element
18642      * @param {String} sGroup the group of related items
18643      * @param {object} config configuration attributes
18644      */
18645     init: function(id, sGroup, config) {
18646         this.initTarget(id, sGroup, config);
18647         if (!Roo.isTouch) {
18648             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18649         }
18650         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18651         // Event.on(this.id, "selectstart", Event.preventDefault);
18652     },
18653
18654     /**
18655      * Initializes Targeting functionality only... the object does not
18656      * get a mousedown handler.
18657      * @method initTarget
18658      * @param id the id of the linked element
18659      * @param {String} sGroup the group of related items
18660      * @param {object} config configuration attributes
18661      */
18662     initTarget: function(id, sGroup, config) {
18663
18664         // configuration attributes
18665         this.config = config || {};
18666
18667         // create a local reference to the drag and drop manager
18668         this.DDM = Roo.dd.DDM;
18669         // initialize the groups array
18670         this.groups = {};
18671
18672         // assume that we have an element reference instead of an id if the
18673         // parameter is not a string
18674         if (typeof id !== "string") {
18675             id = Roo.id(id);
18676         }
18677
18678         // set the id
18679         this.id = id;
18680
18681         // add to an interaction group
18682         this.addToGroup((sGroup) ? sGroup : "default");
18683
18684         // We don't want to register this as the handle with the manager
18685         // so we just set the id rather than calling the setter.
18686         this.handleElId = id;
18687
18688         // the linked element is the element that gets dragged by default
18689         this.setDragElId(id);
18690
18691         // by default, clicked anchors will not start drag operations.
18692         this.invalidHandleTypes = { A: "A" };
18693         this.invalidHandleIds = {};
18694         this.invalidHandleClasses = [];
18695
18696         this.applyConfig();
18697
18698         this.handleOnAvailable();
18699     },
18700
18701     /**
18702      * Applies the configuration parameters that were passed into the constructor.
18703      * This is supposed to happen at each level through the inheritance chain.  So
18704      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18705      * DragDrop in order to get all of the parameters that are available in
18706      * each object.
18707      * @method applyConfig
18708      */
18709     applyConfig: function() {
18710
18711         // configurable properties:
18712         //    padding, isTarget, maintainOffset, primaryButtonOnly
18713         this.padding           = this.config.padding || [0, 0, 0, 0];
18714         this.isTarget          = (this.config.isTarget !== false);
18715         this.maintainOffset    = (this.config.maintainOffset);
18716         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18717
18718     },
18719
18720     /**
18721      * Executed when the linked element is available
18722      * @method handleOnAvailable
18723      * @private
18724      */
18725     handleOnAvailable: function() {
18726         this.available = true;
18727         this.resetConstraints();
18728         this.onAvailable();
18729     },
18730
18731      /**
18732      * Configures the padding for the target zone in px.  Effectively expands
18733      * (or reduces) the virtual object size for targeting calculations.
18734      * Supports css-style shorthand; if only one parameter is passed, all sides
18735      * will have that padding, and if only two are passed, the top and bottom
18736      * will have the first param, the left and right the second.
18737      * @method setPadding
18738      * @param {int} iTop    Top pad
18739      * @param {int} iRight  Right pad
18740      * @param {int} iBot    Bot pad
18741      * @param {int} iLeft   Left pad
18742      */
18743     setPadding: function(iTop, iRight, iBot, iLeft) {
18744         // this.padding = [iLeft, iRight, iTop, iBot];
18745         if (!iRight && 0 !== iRight) {
18746             this.padding = [iTop, iTop, iTop, iTop];
18747         } else if (!iBot && 0 !== iBot) {
18748             this.padding = [iTop, iRight, iTop, iRight];
18749         } else {
18750             this.padding = [iTop, iRight, iBot, iLeft];
18751         }
18752     },
18753
18754     /**
18755      * Stores the initial placement of the linked element.
18756      * @method setInitialPosition
18757      * @param {int} diffX   the X offset, default 0
18758      * @param {int} diffY   the Y offset, default 0
18759      */
18760     setInitPosition: function(diffX, diffY) {
18761         var el = this.getEl();
18762
18763         if (!this.DDM.verifyEl(el)) {
18764             return;
18765         }
18766
18767         var dx = diffX || 0;
18768         var dy = diffY || 0;
18769
18770         var p = Dom.getXY( el );
18771
18772         this.initPageX = p[0] - dx;
18773         this.initPageY = p[1] - dy;
18774
18775         this.lastPageX = p[0];
18776         this.lastPageY = p[1];
18777
18778
18779         this.setStartPosition(p);
18780     },
18781
18782     /**
18783      * Sets the start position of the element.  This is set when the obj
18784      * is initialized, the reset when a drag is started.
18785      * @method setStartPosition
18786      * @param pos current position (from previous lookup)
18787      * @private
18788      */
18789     setStartPosition: function(pos) {
18790         var p = pos || Dom.getXY( this.getEl() );
18791         this.deltaSetXY = null;
18792
18793         this.startPageX = p[0];
18794         this.startPageY = p[1];
18795     },
18796
18797     /**
18798      * Add this instance to a group of related drag/drop objects.  All
18799      * instances belong to at least one group, and can belong to as many
18800      * groups as needed.
18801      * @method addToGroup
18802      * @param sGroup {string} the name of the group
18803      */
18804     addToGroup: function(sGroup) {
18805         this.groups[sGroup] = true;
18806         this.DDM.regDragDrop(this, sGroup);
18807     },
18808
18809     /**
18810      * Remove's this instance from the supplied interaction group
18811      * @method removeFromGroup
18812      * @param {string}  sGroup  The group to drop
18813      */
18814     removeFromGroup: function(sGroup) {
18815         if (this.groups[sGroup]) {
18816             delete this.groups[sGroup];
18817         }
18818
18819         this.DDM.removeDDFromGroup(this, sGroup);
18820     },
18821
18822     /**
18823      * Allows you to specify that an element other than the linked element
18824      * will be moved with the cursor during a drag
18825      * @method setDragElId
18826      * @param id {string} the id of the element that will be used to initiate the drag
18827      */
18828     setDragElId: function(id) {
18829         this.dragElId = id;
18830     },
18831
18832     /**
18833      * Allows you to specify a child of the linked element that should be
18834      * used to initiate the drag operation.  An example of this would be if
18835      * you have a content div with text and links.  Clicking anywhere in the
18836      * content area would normally start the drag operation.  Use this method
18837      * to specify that an element inside of the content div is the element
18838      * that starts the drag operation.
18839      * @method setHandleElId
18840      * @param id {string} the id of the element that will be used to
18841      * initiate the drag.
18842      */
18843     setHandleElId: function(id) {
18844         if (typeof id !== "string") {
18845             id = Roo.id(id);
18846         }
18847         this.handleElId = id;
18848         this.DDM.regHandle(this.id, id);
18849     },
18850
18851     /**
18852      * Allows you to set an element outside of the linked element as a drag
18853      * handle
18854      * @method setOuterHandleElId
18855      * @param id the id of the element that will be used to initiate the drag
18856      */
18857     setOuterHandleElId: function(id) {
18858         if (typeof id !== "string") {
18859             id = Roo.id(id);
18860         }
18861         Event.on(id, "mousedown",
18862                 this.handleMouseDown, this);
18863         this.setHandleElId(id);
18864
18865         this.hasOuterHandles = true;
18866     },
18867
18868     /**
18869      * Remove all drag and drop hooks for this element
18870      * @method unreg
18871      */
18872     unreg: function() {
18873         Event.un(this.id, "mousedown",
18874                 this.handleMouseDown);
18875         Event.un(this.id, "touchstart",
18876                 this.handleMouseDown);
18877         this._domRef = null;
18878         this.DDM._remove(this);
18879     },
18880
18881     destroy : function(){
18882         this.unreg();
18883     },
18884
18885     /**
18886      * Returns true if this instance is locked, or the drag drop mgr is locked
18887      * (meaning that all drag/drop is disabled on the page.)
18888      * @method isLocked
18889      * @return {boolean} true if this obj or all drag/drop is locked, else
18890      * false
18891      */
18892     isLocked: function() {
18893         return (this.DDM.isLocked() || this.locked);
18894     },
18895
18896     /**
18897      * Fired when this object is clicked
18898      * @method handleMouseDown
18899      * @param {Event} e
18900      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18901      * @private
18902      */
18903     handleMouseDown: function(e, oDD){
18904      
18905         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18906             //Roo.log('not touch/ button !=0');
18907             return;
18908         }
18909         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18910             return; // double touch..
18911         }
18912         
18913
18914         if (this.isLocked()) {
18915             //Roo.log('locked');
18916             return;
18917         }
18918
18919         this.DDM.refreshCache(this.groups);
18920 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18921         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18922         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18923             //Roo.log('no outer handes or not over target');
18924                 // do nothing.
18925         } else {
18926 //            Roo.log('check validator');
18927             if (this.clickValidator(e)) {
18928 //                Roo.log('validate success');
18929                 // set the initial element position
18930                 this.setStartPosition();
18931
18932
18933                 this.b4MouseDown(e);
18934                 this.onMouseDown(e);
18935
18936                 this.DDM.handleMouseDown(e, this);
18937
18938                 this.DDM.stopEvent(e);
18939             } else {
18940
18941
18942             }
18943         }
18944     },
18945
18946     clickValidator: function(e) {
18947         var target = e.getTarget();
18948         return ( this.isValidHandleChild(target) &&
18949                     (this.id == this.handleElId ||
18950                         this.DDM.handleWasClicked(target, this.id)) );
18951     },
18952
18953     /**
18954      * Allows you to specify a tag name that should not start a drag operation
18955      * when clicked.  This is designed to facilitate embedding links within a
18956      * drag handle that do something other than start the drag.
18957      * @method addInvalidHandleType
18958      * @param {string} tagName the type of element to exclude
18959      */
18960     addInvalidHandleType: function(tagName) {
18961         var type = tagName.toUpperCase();
18962         this.invalidHandleTypes[type] = type;
18963     },
18964
18965     /**
18966      * Lets you to specify an element id for a child of a drag handle
18967      * that should not initiate a drag
18968      * @method addInvalidHandleId
18969      * @param {string} id the element id of the element you wish to ignore
18970      */
18971     addInvalidHandleId: function(id) {
18972         if (typeof id !== "string") {
18973             id = Roo.id(id);
18974         }
18975         this.invalidHandleIds[id] = id;
18976     },
18977
18978     /**
18979      * Lets you specify a css class of elements that will not initiate a drag
18980      * @method addInvalidHandleClass
18981      * @param {string} cssClass the class of the elements you wish to ignore
18982      */
18983     addInvalidHandleClass: function(cssClass) {
18984         this.invalidHandleClasses.push(cssClass);
18985     },
18986
18987     /**
18988      * Unsets an excluded tag name set by addInvalidHandleType
18989      * @method removeInvalidHandleType
18990      * @param {string} tagName the type of element to unexclude
18991      */
18992     removeInvalidHandleType: function(tagName) {
18993         var type = tagName.toUpperCase();
18994         // this.invalidHandleTypes[type] = null;
18995         delete this.invalidHandleTypes[type];
18996     },
18997
18998     /**
18999      * Unsets an invalid handle id
19000      * @method removeInvalidHandleId
19001      * @param {string} id the id of the element to re-enable
19002      */
19003     removeInvalidHandleId: function(id) {
19004         if (typeof id !== "string") {
19005             id = Roo.id(id);
19006         }
19007         delete this.invalidHandleIds[id];
19008     },
19009
19010     /**
19011      * Unsets an invalid css class
19012      * @method removeInvalidHandleClass
19013      * @param {string} cssClass the class of the element(s) you wish to
19014      * re-enable
19015      */
19016     removeInvalidHandleClass: function(cssClass) {
19017         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19018             if (this.invalidHandleClasses[i] == cssClass) {
19019                 delete this.invalidHandleClasses[i];
19020             }
19021         }
19022     },
19023
19024     /**
19025      * Checks the tag exclusion list to see if this click should be ignored
19026      * @method isValidHandleChild
19027      * @param {HTMLElement} node the HTMLElement to evaluate
19028      * @return {boolean} true if this is a valid tag type, false if not
19029      */
19030     isValidHandleChild: function(node) {
19031
19032         var valid = true;
19033         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19034         var nodeName;
19035         try {
19036             nodeName = node.nodeName.toUpperCase();
19037         } catch(e) {
19038             nodeName = node.nodeName;
19039         }
19040         valid = valid && !this.invalidHandleTypes[nodeName];
19041         valid = valid && !this.invalidHandleIds[node.id];
19042
19043         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19044             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19045         }
19046
19047
19048         return valid;
19049
19050     },
19051
19052     /**
19053      * Create the array of horizontal tick marks if an interval was specified
19054      * in setXConstraint().
19055      * @method setXTicks
19056      * @private
19057      */
19058     setXTicks: function(iStartX, iTickSize) {
19059         this.xTicks = [];
19060         this.xTickSize = iTickSize;
19061
19062         var tickMap = {};
19063
19064         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19065             if (!tickMap[i]) {
19066                 this.xTicks[this.xTicks.length] = i;
19067                 tickMap[i] = true;
19068             }
19069         }
19070
19071         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19072             if (!tickMap[i]) {
19073                 this.xTicks[this.xTicks.length] = i;
19074                 tickMap[i] = true;
19075             }
19076         }
19077
19078         this.xTicks.sort(this.DDM.numericSort) ;
19079     },
19080
19081     /**
19082      * Create the array of vertical tick marks if an interval was specified in
19083      * setYConstraint().
19084      * @method setYTicks
19085      * @private
19086      */
19087     setYTicks: function(iStartY, iTickSize) {
19088         this.yTicks = [];
19089         this.yTickSize = iTickSize;
19090
19091         var tickMap = {};
19092
19093         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19094             if (!tickMap[i]) {
19095                 this.yTicks[this.yTicks.length] = i;
19096                 tickMap[i] = true;
19097             }
19098         }
19099
19100         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19101             if (!tickMap[i]) {
19102                 this.yTicks[this.yTicks.length] = i;
19103                 tickMap[i] = true;
19104             }
19105         }
19106
19107         this.yTicks.sort(this.DDM.numericSort) ;
19108     },
19109
19110     /**
19111      * By default, the element can be dragged any place on the screen.  Use
19112      * this method to limit the horizontal travel of the element.  Pass in
19113      * 0,0 for the parameters if you want to lock the drag to the y axis.
19114      * @method setXConstraint
19115      * @param {int} iLeft the number of pixels the element can move to the left
19116      * @param {int} iRight the number of pixels the element can move to the
19117      * right
19118      * @param {int} iTickSize optional parameter for specifying that the
19119      * element
19120      * should move iTickSize pixels at a time.
19121      */
19122     setXConstraint: function(iLeft, iRight, iTickSize) {
19123         this.leftConstraint = iLeft;
19124         this.rightConstraint = iRight;
19125
19126         this.minX = this.initPageX - iLeft;
19127         this.maxX = this.initPageX + iRight;
19128         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19129
19130         this.constrainX = true;
19131     },
19132
19133     /**
19134      * Clears any constraints applied to this instance.  Also clears ticks
19135      * since they can't exist independent of a constraint at this time.
19136      * @method clearConstraints
19137      */
19138     clearConstraints: function() {
19139         this.constrainX = false;
19140         this.constrainY = false;
19141         this.clearTicks();
19142     },
19143
19144     /**
19145      * Clears any tick interval defined for this instance
19146      * @method clearTicks
19147      */
19148     clearTicks: function() {
19149         this.xTicks = null;
19150         this.yTicks = null;
19151         this.xTickSize = 0;
19152         this.yTickSize = 0;
19153     },
19154
19155     /**
19156      * By default, the element can be dragged any place on the screen.  Set
19157      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19158      * parameters if you want to lock the drag to the x axis.
19159      * @method setYConstraint
19160      * @param {int} iUp the number of pixels the element can move up
19161      * @param {int} iDown the number of pixels the element can move down
19162      * @param {int} iTickSize optional parameter for specifying that the
19163      * element should move iTickSize pixels at a time.
19164      */
19165     setYConstraint: function(iUp, iDown, iTickSize) {
19166         this.topConstraint = iUp;
19167         this.bottomConstraint = iDown;
19168
19169         this.minY = this.initPageY - iUp;
19170         this.maxY = this.initPageY + iDown;
19171         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19172
19173         this.constrainY = true;
19174
19175     },
19176
19177     /**
19178      * resetConstraints must be called if you manually reposition a dd element.
19179      * @method resetConstraints
19180      * @param {boolean} maintainOffset
19181      */
19182     resetConstraints: function() {
19183
19184
19185         // Maintain offsets if necessary
19186         if (this.initPageX || this.initPageX === 0) {
19187             // figure out how much this thing has moved
19188             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19189             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19190
19191             this.setInitPosition(dx, dy);
19192
19193         // This is the first time we have detected the element's position
19194         } else {
19195             this.setInitPosition();
19196         }
19197
19198         if (this.constrainX) {
19199             this.setXConstraint( this.leftConstraint,
19200                                  this.rightConstraint,
19201                                  this.xTickSize        );
19202         }
19203
19204         if (this.constrainY) {
19205             this.setYConstraint( this.topConstraint,
19206                                  this.bottomConstraint,
19207                                  this.yTickSize         );
19208         }
19209     },
19210
19211     /**
19212      * Normally the drag element is moved pixel by pixel, but we can specify
19213      * that it move a number of pixels at a time.  This method resolves the
19214      * location when we have it set up like this.
19215      * @method getTick
19216      * @param {int} val where we want to place the object
19217      * @param {int[]} tickArray sorted array of valid points
19218      * @return {int} the closest tick
19219      * @private
19220      */
19221     getTick: function(val, tickArray) {
19222
19223         if (!tickArray) {
19224             // If tick interval is not defined, it is effectively 1 pixel,
19225             // so we return the value passed to us.
19226             return val;
19227         } else if (tickArray[0] >= val) {
19228             // The value is lower than the first tick, so we return the first
19229             // tick.
19230             return tickArray[0];
19231         } else {
19232             for (var i=0, len=tickArray.length; i<len; ++i) {
19233                 var next = i + 1;
19234                 if (tickArray[next] && tickArray[next] >= val) {
19235                     var diff1 = val - tickArray[i];
19236                     var diff2 = tickArray[next] - val;
19237                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19238                 }
19239             }
19240
19241             // The value is larger than the last tick, so we return the last
19242             // tick.
19243             return tickArray[tickArray.length - 1];
19244         }
19245     },
19246
19247     /**
19248      * toString method
19249      * @method toString
19250      * @return {string} string representation of the dd obj
19251      */
19252     toString: function() {
19253         return ("DragDrop " + this.id);
19254     }
19255
19256 });
19257
19258 })();
19259 /*
19260  * Based on:
19261  * Ext JS Library 1.1.1
19262  * Copyright(c) 2006-2007, Ext JS, LLC.
19263  *
19264  * Originally Released Under LGPL - original licence link has changed is not relivant.
19265  *
19266  * Fork - LGPL
19267  * <script type="text/javascript">
19268  */
19269
19270
19271 /**
19272  * The drag and drop utility provides a framework for building drag and drop
19273  * applications.  In addition to enabling drag and drop for specific elements,
19274  * the drag and drop elements are tracked by the manager class, and the
19275  * interactions between the various elements are tracked during the drag and
19276  * the implementing code is notified about these important moments.
19277  */
19278
19279 // Only load the library once.  Rewriting the manager class would orphan
19280 // existing drag and drop instances.
19281 if (!Roo.dd.DragDropMgr) {
19282
19283 /**
19284  * @class Roo.dd.DragDropMgr
19285  * DragDropMgr is a singleton that tracks the element interaction for
19286  * all DragDrop items in the window.  Generally, you will not call
19287  * this class directly, but it does have helper methods that could
19288  * be useful in your DragDrop implementations.
19289  * @singleton
19290  */
19291 Roo.dd.DragDropMgr = function() {
19292
19293     var Event = Roo.EventManager;
19294
19295     return {
19296
19297         /**
19298          * Two dimensional Array of registered DragDrop objects.  The first
19299          * dimension is the DragDrop item group, the second the DragDrop
19300          * object.
19301          * @property ids
19302          * @type {string: string}
19303          * @private
19304          * @static
19305          */
19306         ids: {},
19307
19308         /**
19309          * Array of element ids defined as drag handles.  Used to determine
19310          * if the element that generated the mousedown event is actually the
19311          * handle and not the html element itself.
19312          * @property handleIds
19313          * @type {string: string}
19314          * @private
19315          * @static
19316          */
19317         handleIds: {},
19318
19319         /**
19320          * the DragDrop object that is currently being dragged
19321          * @property dragCurrent
19322          * @type DragDrop
19323          * @private
19324          * @static
19325          **/
19326         dragCurrent: null,
19327
19328         /**
19329          * the DragDrop object(s) that are being hovered over
19330          * @property dragOvers
19331          * @type Array
19332          * @private
19333          * @static
19334          */
19335         dragOvers: {},
19336
19337         /**
19338          * the X distance between the cursor and the object being dragged
19339          * @property deltaX
19340          * @type int
19341          * @private
19342          * @static
19343          */
19344         deltaX: 0,
19345
19346         /**
19347          * the Y distance between the cursor and the object being dragged
19348          * @property deltaY
19349          * @type int
19350          * @private
19351          * @static
19352          */
19353         deltaY: 0,
19354
19355         /**
19356          * Flag to determine if we should prevent the default behavior of the
19357          * events we define. By default this is true, but this can be set to
19358          * false if you need the default behavior (not recommended)
19359          * @property preventDefault
19360          * @type boolean
19361          * @static
19362          */
19363         preventDefault: true,
19364
19365         /**
19366          * Flag to determine if we should stop the propagation of the events
19367          * we generate. This is true by default but you may want to set it to
19368          * false if the html element contains other features that require the
19369          * mouse click.
19370          * @property stopPropagation
19371          * @type boolean
19372          * @static
19373          */
19374         stopPropagation: true,
19375
19376         /**
19377          * Internal flag that is set to true when drag and drop has been
19378          * intialized
19379          * @property initialized
19380          * @private
19381          * @static
19382          */
19383         initalized: false,
19384
19385         /**
19386          * All drag and drop can be disabled.
19387          * @property locked
19388          * @private
19389          * @static
19390          */
19391         locked: false,
19392
19393         /**
19394          * Called the first time an element is registered.
19395          * @method init
19396          * @private
19397          * @static
19398          */
19399         init: function() {
19400             this.initialized = true;
19401         },
19402
19403         /**
19404          * In point mode, drag and drop interaction is defined by the
19405          * location of the cursor during the drag/drop
19406          * @property POINT
19407          * @type int
19408          * @static
19409          */
19410         POINT: 0,
19411
19412         /**
19413          * In intersect mode, drag and drop interactio nis defined by the
19414          * overlap of two or more drag and drop objects.
19415          * @property INTERSECT
19416          * @type int
19417          * @static
19418          */
19419         INTERSECT: 1,
19420
19421         /**
19422          * The current drag and drop mode.  Default: POINT
19423          * @property mode
19424          * @type int
19425          * @static
19426          */
19427         mode: 0,
19428
19429         /**
19430          * Runs method on all drag and drop objects
19431          * @method _execOnAll
19432          * @private
19433          * @static
19434          */
19435         _execOnAll: function(sMethod, args) {
19436             for (var i in this.ids) {
19437                 for (var j in this.ids[i]) {
19438                     var oDD = this.ids[i][j];
19439                     if (! this.isTypeOfDD(oDD)) {
19440                         continue;
19441                     }
19442                     oDD[sMethod].apply(oDD, args);
19443                 }
19444             }
19445         },
19446
19447         /**
19448          * Drag and drop initialization.  Sets up the global event handlers
19449          * @method _onLoad
19450          * @private
19451          * @static
19452          */
19453         _onLoad: function() {
19454
19455             this.init();
19456
19457             if (!Roo.isTouch) {
19458                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19459                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19460             }
19461             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19462             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19463             
19464             Event.on(window,   "unload",    this._onUnload, this, true);
19465             Event.on(window,   "resize",    this._onResize, this, true);
19466             // Event.on(window,   "mouseout",    this._test);
19467
19468         },
19469
19470         /**
19471          * Reset constraints on all drag and drop objs
19472          * @method _onResize
19473          * @private
19474          * @static
19475          */
19476         _onResize: function(e) {
19477             this._execOnAll("resetConstraints", []);
19478         },
19479
19480         /**
19481          * Lock all drag and drop functionality
19482          * @method lock
19483          * @static
19484          */
19485         lock: function() { this.locked = true; },
19486
19487         /**
19488          * Unlock all drag and drop functionality
19489          * @method unlock
19490          * @static
19491          */
19492         unlock: function() { this.locked = false; },
19493
19494         /**
19495          * Is drag and drop locked?
19496          * @method isLocked
19497          * @return {boolean} True if drag and drop is locked, false otherwise.
19498          * @static
19499          */
19500         isLocked: function() { return this.locked; },
19501
19502         /**
19503          * Location cache that is set for all drag drop objects when a drag is
19504          * initiated, cleared when the drag is finished.
19505          * @property locationCache
19506          * @private
19507          * @static
19508          */
19509         locationCache: {},
19510
19511         /**
19512          * Set useCache to false if you want to force object the lookup of each
19513          * drag and drop linked element constantly during a drag.
19514          * @property useCache
19515          * @type boolean
19516          * @static
19517          */
19518         useCache: true,
19519
19520         /**
19521          * The number of pixels that the mouse needs to move after the
19522          * mousedown before the drag is initiated.  Default=3;
19523          * @property clickPixelThresh
19524          * @type int
19525          * @static
19526          */
19527         clickPixelThresh: 3,
19528
19529         /**
19530          * The number of milliseconds after the mousedown event to initiate the
19531          * drag if we don't get a mouseup event. Default=1000
19532          * @property clickTimeThresh
19533          * @type int
19534          * @static
19535          */
19536         clickTimeThresh: 350,
19537
19538         /**
19539          * Flag that indicates that either the drag pixel threshold or the
19540          * mousdown time threshold has been met
19541          * @property dragThreshMet
19542          * @type boolean
19543          * @private
19544          * @static
19545          */
19546         dragThreshMet: false,
19547
19548         /**
19549          * Timeout used for the click time threshold
19550          * @property clickTimeout
19551          * @type Object
19552          * @private
19553          * @static
19554          */
19555         clickTimeout: null,
19556
19557         /**
19558          * The X position of the mousedown event stored for later use when a
19559          * drag threshold is met.
19560          * @property startX
19561          * @type int
19562          * @private
19563          * @static
19564          */
19565         startX: 0,
19566
19567         /**
19568          * The Y position of the mousedown event stored for later use when a
19569          * drag threshold is met.
19570          * @property startY
19571          * @type int
19572          * @private
19573          * @static
19574          */
19575         startY: 0,
19576
19577         /**
19578          * Each DragDrop instance must be registered with the DragDropMgr.
19579          * This is executed in DragDrop.init()
19580          * @method regDragDrop
19581          * @param {DragDrop} oDD the DragDrop object to register
19582          * @param {String} sGroup the name of the group this element belongs to
19583          * @static
19584          */
19585         regDragDrop: function(oDD, sGroup) {
19586             if (!this.initialized) { this.init(); }
19587
19588             if (!this.ids[sGroup]) {
19589                 this.ids[sGroup] = {};
19590             }
19591             this.ids[sGroup][oDD.id] = oDD;
19592         },
19593
19594         /**
19595          * Removes the supplied dd instance from the supplied group. Executed
19596          * by DragDrop.removeFromGroup, so don't call this function directly.
19597          * @method removeDDFromGroup
19598          * @private
19599          * @static
19600          */
19601         removeDDFromGroup: function(oDD, sGroup) {
19602             if (!this.ids[sGroup]) {
19603                 this.ids[sGroup] = {};
19604             }
19605
19606             var obj = this.ids[sGroup];
19607             if (obj && obj[oDD.id]) {
19608                 delete obj[oDD.id];
19609             }
19610         },
19611
19612         /**
19613          * Unregisters a drag and drop item.  This is executed in
19614          * DragDrop.unreg, use that method instead of calling this directly.
19615          * @method _remove
19616          * @private
19617          * @static
19618          */
19619         _remove: function(oDD) {
19620             for (var g in oDD.groups) {
19621                 if (g && this.ids[g][oDD.id]) {
19622                     delete this.ids[g][oDD.id];
19623                 }
19624             }
19625             delete this.handleIds[oDD.id];
19626         },
19627
19628         /**
19629          * Each DragDrop handle element must be registered.  This is done
19630          * automatically when executing DragDrop.setHandleElId()
19631          * @method regHandle
19632          * @param {String} sDDId the DragDrop id this element is a handle for
19633          * @param {String} sHandleId the id of the element that is the drag
19634          * handle
19635          * @static
19636          */
19637         regHandle: function(sDDId, sHandleId) {
19638             if (!this.handleIds[sDDId]) {
19639                 this.handleIds[sDDId] = {};
19640             }
19641             this.handleIds[sDDId][sHandleId] = sHandleId;
19642         },
19643
19644         /**
19645          * Utility function to determine if a given element has been
19646          * registered as a drag drop item.
19647          * @method isDragDrop
19648          * @param {String} id the element id to check
19649          * @return {boolean} true if this element is a DragDrop item,
19650          * false otherwise
19651          * @static
19652          */
19653         isDragDrop: function(id) {
19654             return ( this.getDDById(id) ) ? true : false;
19655         },
19656
19657         /**
19658          * Returns the drag and drop instances that are in all groups the
19659          * passed in instance belongs to.
19660          * @method getRelated
19661          * @param {DragDrop} p_oDD the obj to get related data for
19662          * @param {boolean} bTargetsOnly if true, only return targetable objs
19663          * @return {DragDrop[]} the related instances
19664          * @static
19665          */
19666         getRelated: function(p_oDD, bTargetsOnly) {
19667             var oDDs = [];
19668             for (var i in p_oDD.groups) {
19669                 for (j in this.ids[i]) {
19670                     var dd = this.ids[i][j];
19671                     if (! this.isTypeOfDD(dd)) {
19672                         continue;
19673                     }
19674                     if (!bTargetsOnly || dd.isTarget) {
19675                         oDDs[oDDs.length] = dd;
19676                     }
19677                 }
19678             }
19679
19680             return oDDs;
19681         },
19682
19683         /**
19684          * Returns true if the specified dd target is a legal target for
19685          * the specifice drag obj
19686          * @method isLegalTarget
19687          * @param {DragDrop} the drag obj
19688          * @param {DragDrop} the target
19689          * @return {boolean} true if the target is a legal target for the
19690          * dd obj
19691          * @static
19692          */
19693         isLegalTarget: function (oDD, oTargetDD) {
19694             var targets = this.getRelated(oDD, true);
19695             for (var i=0, len=targets.length;i<len;++i) {
19696                 if (targets[i].id == oTargetDD.id) {
19697                     return true;
19698                 }
19699             }
19700
19701             return false;
19702         },
19703
19704         /**
19705          * My goal is to be able to transparently determine if an object is
19706          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19707          * returns "object", oDD.constructor.toString() always returns
19708          * "DragDrop" and not the name of the subclass.  So for now it just
19709          * evaluates a well-known variable in DragDrop.
19710          * @method isTypeOfDD
19711          * @param {Object} the object to evaluate
19712          * @return {boolean} true if typeof oDD = DragDrop
19713          * @static
19714          */
19715         isTypeOfDD: function (oDD) {
19716             return (oDD && oDD.__ygDragDrop);
19717         },
19718
19719         /**
19720          * Utility function to determine if a given element has been
19721          * registered as a drag drop handle for the given Drag Drop object.
19722          * @method isHandle
19723          * @param {String} id the element id to check
19724          * @return {boolean} true if this element is a DragDrop handle, false
19725          * otherwise
19726          * @static
19727          */
19728         isHandle: function(sDDId, sHandleId) {
19729             return ( this.handleIds[sDDId] &&
19730                             this.handleIds[sDDId][sHandleId] );
19731         },
19732
19733         /**
19734          * Returns the DragDrop instance for a given id
19735          * @method getDDById
19736          * @param {String} id the id of the DragDrop object
19737          * @return {DragDrop} the drag drop object, null if it is not found
19738          * @static
19739          */
19740         getDDById: function(id) {
19741             for (var i in this.ids) {
19742                 if (this.ids[i][id]) {
19743                     return this.ids[i][id];
19744                 }
19745             }
19746             return null;
19747         },
19748
19749         /**
19750          * Fired after a registered DragDrop object gets the mousedown event.
19751          * Sets up the events required to track the object being dragged
19752          * @method handleMouseDown
19753          * @param {Event} e the event
19754          * @param oDD the DragDrop object being dragged
19755          * @private
19756          * @static
19757          */
19758         handleMouseDown: function(e, oDD) {
19759             if(Roo.QuickTips){
19760                 Roo.QuickTips.disable();
19761             }
19762             this.currentTarget = e.getTarget();
19763
19764             this.dragCurrent = oDD;
19765
19766             var el = oDD.getEl();
19767
19768             // track start position
19769             this.startX = e.getPageX();
19770             this.startY = e.getPageY();
19771
19772             this.deltaX = this.startX - el.offsetLeft;
19773             this.deltaY = this.startY - el.offsetTop;
19774
19775             this.dragThreshMet = false;
19776
19777             this.clickTimeout = setTimeout(
19778                     function() {
19779                         var DDM = Roo.dd.DDM;
19780                         DDM.startDrag(DDM.startX, DDM.startY);
19781                     },
19782                     this.clickTimeThresh );
19783         },
19784
19785         /**
19786          * Fired when either the drag pixel threshol or the mousedown hold
19787          * time threshold has been met.
19788          * @method startDrag
19789          * @param x {int} the X position of the original mousedown
19790          * @param y {int} the Y position of the original mousedown
19791          * @static
19792          */
19793         startDrag: function(x, y) {
19794             clearTimeout(this.clickTimeout);
19795             if (this.dragCurrent) {
19796                 this.dragCurrent.b4StartDrag(x, y);
19797                 this.dragCurrent.startDrag(x, y);
19798             }
19799             this.dragThreshMet = true;
19800         },
19801
19802         /**
19803          * Internal function to handle the mouseup event.  Will be invoked
19804          * from the context of the document.
19805          * @method handleMouseUp
19806          * @param {Event} e the event
19807          * @private
19808          * @static
19809          */
19810         handleMouseUp: function(e) {
19811
19812             if(Roo.QuickTips){
19813                 Roo.QuickTips.enable();
19814             }
19815             if (! this.dragCurrent) {
19816                 return;
19817             }
19818
19819             clearTimeout(this.clickTimeout);
19820
19821             if (this.dragThreshMet) {
19822                 this.fireEvents(e, true);
19823             } else {
19824             }
19825
19826             this.stopDrag(e);
19827
19828             this.stopEvent(e);
19829         },
19830
19831         /**
19832          * Utility to stop event propagation and event default, if these
19833          * features are turned on.
19834          * @method stopEvent
19835          * @param {Event} e the event as returned by this.getEvent()
19836          * @static
19837          */
19838         stopEvent: function(e){
19839             if(this.stopPropagation) {
19840                 e.stopPropagation();
19841             }
19842
19843             if (this.preventDefault) {
19844                 e.preventDefault();
19845             }
19846         },
19847
19848         /**
19849          * Internal function to clean up event handlers after the drag
19850          * operation is complete
19851          * @method stopDrag
19852          * @param {Event} e the event
19853          * @private
19854          * @static
19855          */
19856         stopDrag: function(e) {
19857             // Fire the drag end event for the item that was dragged
19858             if (this.dragCurrent) {
19859                 if (this.dragThreshMet) {
19860                     this.dragCurrent.b4EndDrag(e);
19861                     this.dragCurrent.endDrag(e);
19862                 }
19863
19864                 this.dragCurrent.onMouseUp(e);
19865             }
19866
19867             this.dragCurrent = null;
19868             this.dragOvers = {};
19869         },
19870
19871         /**
19872          * Internal function to handle the mousemove event.  Will be invoked
19873          * from the context of the html element.
19874          *
19875          * @TODO figure out what we can do about mouse events lost when the
19876          * user drags objects beyond the window boundary.  Currently we can
19877          * detect this in internet explorer by verifying that the mouse is
19878          * down during the mousemove event.  Firefox doesn't give us the
19879          * button state on the mousemove event.
19880          * @method handleMouseMove
19881          * @param {Event} e the event
19882          * @private
19883          * @static
19884          */
19885         handleMouseMove: function(e) {
19886             if (! this.dragCurrent) {
19887                 return true;
19888             }
19889
19890             // var button = e.which || e.button;
19891
19892             // check for IE mouseup outside of page boundary
19893             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19894                 this.stopEvent(e);
19895                 return this.handleMouseUp(e);
19896             }
19897
19898             if (!this.dragThreshMet) {
19899                 var diffX = Math.abs(this.startX - e.getPageX());
19900                 var diffY = Math.abs(this.startY - e.getPageY());
19901                 if (diffX > this.clickPixelThresh ||
19902                             diffY > this.clickPixelThresh) {
19903                     this.startDrag(this.startX, this.startY);
19904                 }
19905             }
19906
19907             if (this.dragThreshMet) {
19908                 this.dragCurrent.b4Drag(e);
19909                 this.dragCurrent.onDrag(e);
19910                 if(!this.dragCurrent.moveOnly){
19911                     this.fireEvents(e, false);
19912                 }
19913             }
19914
19915             this.stopEvent(e);
19916
19917             return true;
19918         },
19919
19920         /**
19921          * Iterates over all of the DragDrop elements to find ones we are
19922          * hovering over or dropping on
19923          * @method fireEvents
19924          * @param {Event} e the event
19925          * @param {boolean} isDrop is this a drop op or a mouseover op?
19926          * @private
19927          * @static
19928          */
19929         fireEvents: function(e, isDrop) {
19930             var dc = this.dragCurrent;
19931
19932             // If the user did the mouse up outside of the window, we could
19933             // get here even though we have ended the drag.
19934             if (!dc || dc.isLocked()) {
19935                 return;
19936             }
19937
19938             var pt = e.getPoint();
19939
19940             // cache the previous dragOver array
19941             var oldOvers = [];
19942
19943             var outEvts   = [];
19944             var overEvts  = [];
19945             var dropEvts  = [];
19946             var enterEvts = [];
19947
19948             // Check to see if the object(s) we were hovering over is no longer
19949             // being hovered over so we can fire the onDragOut event
19950             for (var i in this.dragOvers) {
19951
19952                 var ddo = this.dragOvers[i];
19953
19954                 if (! this.isTypeOfDD(ddo)) {
19955                     continue;
19956                 }
19957
19958                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19959                     outEvts.push( ddo );
19960                 }
19961
19962                 oldOvers[i] = true;
19963                 delete this.dragOvers[i];
19964             }
19965
19966             for (var sGroup in dc.groups) {
19967
19968                 if ("string" != typeof sGroup) {
19969                     continue;
19970                 }
19971
19972                 for (i in this.ids[sGroup]) {
19973                     var oDD = this.ids[sGroup][i];
19974                     if (! this.isTypeOfDD(oDD)) {
19975                         continue;
19976                     }
19977
19978                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19979                         if (this.isOverTarget(pt, oDD, this.mode)) {
19980                             // look for drop interactions
19981                             if (isDrop) {
19982                                 dropEvts.push( oDD );
19983                             // look for drag enter and drag over interactions
19984                             } else {
19985
19986                                 // initial drag over: dragEnter fires
19987                                 if (!oldOvers[oDD.id]) {
19988                                     enterEvts.push( oDD );
19989                                 // subsequent drag overs: dragOver fires
19990                                 } else {
19991                                     overEvts.push( oDD );
19992                                 }
19993
19994                                 this.dragOvers[oDD.id] = oDD;
19995                             }
19996                         }
19997                     }
19998                 }
19999             }
20000
20001             if (this.mode) {
20002                 if (outEvts.length) {
20003                     dc.b4DragOut(e, outEvts);
20004                     dc.onDragOut(e, outEvts);
20005                 }
20006
20007                 if (enterEvts.length) {
20008                     dc.onDragEnter(e, enterEvts);
20009                 }
20010
20011                 if (overEvts.length) {
20012                     dc.b4DragOver(e, overEvts);
20013                     dc.onDragOver(e, overEvts);
20014                 }
20015
20016                 if (dropEvts.length) {
20017                     dc.b4DragDrop(e, dropEvts);
20018                     dc.onDragDrop(e, dropEvts);
20019                 }
20020
20021             } else {
20022                 // fire dragout events
20023                 var len = 0;
20024                 for (i=0, len=outEvts.length; i<len; ++i) {
20025                     dc.b4DragOut(e, outEvts[i].id);
20026                     dc.onDragOut(e, outEvts[i].id);
20027                 }
20028
20029                 // fire enter events
20030                 for (i=0,len=enterEvts.length; i<len; ++i) {
20031                     // dc.b4DragEnter(e, oDD.id);
20032                     dc.onDragEnter(e, enterEvts[i].id);
20033                 }
20034
20035                 // fire over events
20036                 for (i=0,len=overEvts.length; i<len; ++i) {
20037                     dc.b4DragOver(e, overEvts[i].id);
20038                     dc.onDragOver(e, overEvts[i].id);
20039                 }
20040
20041                 // fire drop events
20042                 for (i=0, len=dropEvts.length; i<len; ++i) {
20043                     dc.b4DragDrop(e, dropEvts[i].id);
20044                     dc.onDragDrop(e, dropEvts[i].id);
20045                 }
20046
20047             }
20048
20049             // notify about a drop that did not find a target
20050             if (isDrop && !dropEvts.length) {
20051                 dc.onInvalidDrop(e);
20052             }
20053
20054         },
20055
20056         /**
20057          * Helper function for getting the best match from the list of drag
20058          * and drop objects returned by the drag and drop events when we are
20059          * in INTERSECT mode.  It returns either the first object that the
20060          * cursor is over, or the object that has the greatest overlap with
20061          * the dragged element.
20062          * @method getBestMatch
20063          * @param  {DragDrop[]} dds The array of drag and drop objects
20064          * targeted
20065          * @return {DragDrop}       The best single match
20066          * @static
20067          */
20068         getBestMatch: function(dds) {
20069             var winner = null;
20070             // Return null if the input is not what we expect
20071             //if (!dds || !dds.length || dds.length == 0) {
20072                // winner = null;
20073             // If there is only one item, it wins
20074             //} else if (dds.length == 1) {
20075
20076             var len = dds.length;
20077
20078             if (len == 1) {
20079                 winner = dds[0];
20080             } else {
20081                 // Loop through the targeted items
20082                 for (var i=0; i<len; ++i) {
20083                     var dd = dds[i];
20084                     // If the cursor is over the object, it wins.  If the
20085                     // cursor is over multiple matches, the first one we come
20086                     // to wins.
20087                     if (dd.cursorIsOver) {
20088                         winner = dd;
20089                         break;
20090                     // Otherwise the object with the most overlap wins
20091                     } else {
20092                         if (!winner ||
20093                             winner.overlap.getArea() < dd.overlap.getArea()) {
20094                             winner = dd;
20095                         }
20096                     }
20097                 }
20098             }
20099
20100             return winner;
20101         },
20102
20103         /**
20104          * Refreshes the cache of the top-left and bottom-right points of the
20105          * drag and drop objects in the specified group(s).  This is in the
20106          * format that is stored in the drag and drop instance, so typical
20107          * usage is:
20108          * <code>
20109          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20110          * </code>
20111          * Alternatively:
20112          * <code>
20113          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20114          * </code>
20115          * @TODO this really should be an indexed array.  Alternatively this
20116          * method could accept both.
20117          * @method refreshCache
20118          * @param {Object} groups an associative array of groups to refresh
20119          * @static
20120          */
20121         refreshCache: function(groups) {
20122             for (var sGroup in groups) {
20123                 if ("string" != typeof sGroup) {
20124                     continue;
20125                 }
20126                 for (var i in this.ids[sGroup]) {
20127                     var oDD = this.ids[sGroup][i];
20128
20129                     if (this.isTypeOfDD(oDD)) {
20130                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20131                         var loc = this.getLocation(oDD);
20132                         if (loc) {
20133                             this.locationCache[oDD.id] = loc;
20134                         } else {
20135                             delete this.locationCache[oDD.id];
20136                             // this will unregister the drag and drop object if
20137                             // the element is not in a usable state
20138                             // oDD.unreg();
20139                         }
20140                     }
20141                 }
20142             }
20143         },
20144
20145         /**
20146          * This checks to make sure an element exists and is in the DOM.  The
20147          * main purpose is to handle cases where innerHTML is used to remove
20148          * drag and drop objects from the DOM.  IE provides an 'unspecified
20149          * error' when trying to access the offsetParent of such an element
20150          * @method verifyEl
20151          * @param {HTMLElement} el the element to check
20152          * @return {boolean} true if the element looks usable
20153          * @static
20154          */
20155         verifyEl: function(el) {
20156             if (el) {
20157                 var parent;
20158                 if(Roo.isIE){
20159                     try{
20160                         parent = el.offsetParent;
20161                     }catch(e){}
20162                 }else{
20163                     parent = el.offsetParent;
20164                 }
20165                 if (parent) {
20166                     return true;
20167                 }
20168             }
20169
20170             return false;
20171         },
20172
20173         /**
20174          * Returns a Region object containing the drag and drop element's position
20175          * and size, including the padding configured for it
20176          * @method getLocation
20177          * @param {DragDrop} oDD the drag and drop object to get the
20178          *                       location for
20179          * @return {Roo.lib.Region} a Region object representing the total area
20180          *                             the element occupies, including any padding
20181          *                             the instance is configured for.
20182          * @static
20183          */
20184         getLocation: function(oDD) {
20185             if (! this.isTypeOfDD(oDD)) {
20186                 return null;
20187             }
20188
20189             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20190
20191             try {
20192                 pos= Roo.lib.Dom.getXY(el);
20193             } catch (e) { }
20194
20195             if (!pos) {
20196                 return null;
20197             }
20198
20199             x1 = pos[0];
20200             x2 = x1 + el.offsetWidth;
20201             y1 = pos[1];
20202             y2 = y1 + el.offsetHeight;
20203
20204             t = y1 - oDD.padding[0];
20205             r = x2 + oDD.padding[1];
20206             b = y2 + oDD.padding[2];
20207             l = x1 - oDD.padding[3];
20208
20209             return new Roo.lib.Region( t, r, b, l );
20210         },
20211
20212         /**
20213          * Checks the cursor location to see if it over the target
20214          * @method isOverTarget
20215          * @param {Roo.lib.Point} pt The point to evaluate
20216          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20217          * @return {boolean} true if the mouse is over the target
20218          * @private
20219          * @static
20220          */
20221         isOverTarget: function(pt, oTarget, intersect) {
20222             // use cache if available
20223             var loc = this.locationCache[oTarget.id];
20224             if (!loc || !this.useCache) {
20225                 loc = this.getLocation(oTarget);
20226                 this.locationCache[oTarget.id] = loc;
20227
20228             }
20229
20230             if (!loc) {
20231                 return false;
20232             }
20233
20234             oTarget.cursorIsOver = loc.contains( pt );
20235
20236             // DragDrop is using this as a sanity check for the initial mousedown
20237             // in this case we are done.  In POINT mode, if the drag obj has no
20238             // contraints, we are also done. Otherwise we need to evaluate the
20239             // location of the target as related to the actual location of the
20240             // dragged element.
20241             var dc = this.dragCurrent;
20242             if (!dc || !dc.getTargetCoord ||
20243                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20244                 return oTarget.cursorIsOver;
20245             }
20246
20247             oTarget.overlap = null;
20248
20249             // Get the current location of the drag element, this is the
20250             // location of the mouse event less the delta that represents
20251             // where the original mousedown happened on the element.  We
20252             // need to consider constraints and ticks as well.
20253             var pos = dc.getTargetCoord(pt.x, pt.y);
20254
20255             var el = dc.getDragEl();
20256             var curRegion = new Roo.lib.Region( pos.y,
20257                                                    pos.x + el.offsetWidth,
20258                                                    pos.y + el.offsetHeight,
20259                                                    pos.x );
20260
20261             var overlap = curRegion.intersect(loc);
20262
20263             if (overlap) {
20264                 oTarget.overlap = overlap;
20265                 return (intersect) ? true : oTarget.cursorIsOver;
20266             } else {
20267                 return false;
20268             }
20269         },
20270
20271         /**
20272          * unload event handler
20273          * @method _onUnload
20274          * @private
20275          * @static
20276          */
20277         _onUnload: function(e, me) {
20278             Roo.dd.DragDropMgr.unregAll();
20279         },
20280
20281         /**
20282          * Cleans up the drag and drop events and objects.
20283          * @method unregAll
20284          * @private
20285          * @static
20286          */
20287         unregAll: function() {
20288
20289             if (this.dragCurrent) {
20290                 this.stopDrag();
20291                 this.dragCurrent = null;
20292             }
20293
20294             this._execOnAll("unreg", []);
20295
20296             for (i in this.elementCache) {
20297                 delete this.elementCache[i];
20298             }
20299
20300             this.elementCache = {};
20301             this.ids = {};
20302         },
20303
20304         /**
20305          * A cache of DOM elements
20306          * @property elementCache
20307          * @private
20308          * @static
20309          */
20310         elementCache: {},
20311
20312         /**
20313          * Get the wrapper for the DOM element specified
20314          * @method getElWrapper
20315          * @param {String} id the id of the element to get
20316          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20317          * @private
20318          * @deprecated This wrapper isn't that useful
20319          * @static
20320          */
20321         getElWrapper: function(id) {
20322             var oWrapper = this.elementCache[id];
20323             if (!oWrapper || !oWrapper.el) {
20324                 oWrapper = this.elementCache[id] =
20325                     new this.ElementWrapper(Roo.getDom(id));
20326             }
20327             return oWrapper;
20328         },
20329
20330         /**
20331          * Returns the actual DOM element
20332          * @method getElement
20333          * @param {String} id the id of the elment to get
20334          * @return {Object} The element
20335          * @deprecated use Roo.getDom instead
20336          * @static
20337          */
20338         getElement: function(id) {
20339             return Roo.getDom(id);
20340         },
20341
20342         /**
20343          * Returns the style property for the DOM element (i.e.,
20344          * document.getElById(id).style)
20345          * @method getCss
20346          * @param {String} id the id of the elment to get
20347          * @return {Object} The style property of the element
20348          * @deprecated use Roo.getDom instead
20349          * @static
20350          */
20351         getCss: function(id) {
20352             var el = Roo.getDom(id);
20353             return (el) ? el.style : null;
20354         },
20355
20356         /**
20357          * Inner class for cached elements
20358          * @class DragDropMgr.ElementWrapper
20359          * @for DragDropMgr
20360          * @private
20361          * @deprecated
20362          */
20363         ElementWrapper: function(el) {
20364                 /**
20365                  * The element
20366                  * @property el
20367                  */
20368                 this.el = el || null;
20369                 /**
20370                  * The element id
20371                  * @property id
20372                  */
20373                 this.id = this.el && el.id;
20374                 /**
20375                  * A reference to the style property
20376                  * @property css
20377                  */
20378                 this.css = this.el && el.style;
20379             },
20380
20381         /**
20382          * Returns the X position of an html element
20383          * @method getPosX
20384          * @param el the element for which to get the position
20385          * @return {int} the X coordinate
20386          * @for DragDropMgr
20387          * @deprecated use Roo.lib.Dom.getX instead
20388          * @static
20389          */
20390         getPosX: function(el) {
20391             return Roo.lib.Dom.getX(el);
20392         },
20393
20394         /**
20395          * Returns the Y position of an html element
20396          * @method getPosY
20397          * @param el the element for which to get the position
20398          * @return {int} the Y coordinate
20399          * @deprecated use Roo.lib.Dom.getY instead
20400          * @static
20401          */
20402         getPosY: function(el) {
20403             return Roo.lib.Dom.getY(el);
20404         },
20405
20406         /**
20407          * Swap two nodes.  In IE, we use the native method, for others we
20408          * emulate the IE behavior
20409          * @method swapNode
20410          * @param n1 the first node to swap
20411          * @param n2 the other node to swap
20412          * @static
20413          */
20414         swapNode: function(n1, n2) {
20415             if (n1.swapNode) {
20416                 n1.swapNode(n2);
20417             } else {
20418                 var p = n2.parentNode;
20419                 var s = n2.nextSibling;
20420
20421                 if (s == n1) {
20422                     p.insertBefore(n1, n2);
20423                 } else if (n2 == n1.nextSibling) {
20424                     p.insertBefore(n2, n1);
20425                 } else {
20426                     n1.parentNode.replaceChild(n2, n1);
20427                     p.insertBefore(n1, s);
20428                 }
20429             }
20430         },
20431
20432         /**
20433          * Returns the current scroll position
20434          * @method getScroll
20435          * @private
20436          * @static
20437          */
20438         getScroll: function () {
20439             var t, l, dde=document.documentElement, db=document.body;
20440             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20441                 t = dde.scrollTop;
20442                 l = dde.scrollLeft;
20443             } else if (db) {
20444                 t = db.scrollTop;
20445                 l = db.scrollLeft;
20446             } else {
20447
20448             }
20449             return { top: t, left: l };
20450         },
20451
20452         /**
20453          * Returns the specified element style property
20454          * @method getStyle
20455          * @param {HTMLElement} el          the element
20456          * @param {string}      styleProp   the style property
20457          * @return {string} The value of the style property
20458          * @deprecated use Roo.lib.Dom.getStyle
20459          * @static
20460          */
20461         getStyle: function(el, styleProp) {
20462             return Roo.fly(el).getStyle(styleProp);
20463         },
20464
20465         /**
20466          * Gets the scrollTop
20467          * @method getScrollTop
20468          * @return {int} the document's scrollTop
20469          * @static
20470          */
20471         getScrollTop: function () { return this.getScroll().top; },
20472
20473         /**
20474          * Gets the scrollLeft
20475          * @method getScrollLeft
20476          * @return {int} the document's scrollTop
20477          * @static
20478          */
20479         getScrollLeft: function () { return this.getScroll().left; },
20480
20481         /**
20482          * Sets the x/y position of an element to the location of the
20483          * target element.
20484          * @method moveToEl
20485          * @param {HTMLElement} moveEl      The element to move
20486          * @param {HTMLElement} targetEl    The position reference element
20487          * @static
20488          */
20489         moveToEl: function (moveEl, targetEl) {
20490             var aCoord = Roo.lib.Dom.getXY(targetEl);
20491             Roo.lib.Dom.setXY(moveEl, aCoord);
20492         },
20493
20494         /**
20495          * Numeric array sort function
20496          * @method numericSort
20497          * @static
20498          */
20499         numericSort: function(a, b) { return (a - b); },
20500
20501         /**
20502          * Internal counter
20503          * @property _timeoutCount
20504          * @private
20505          * @static
20506          */
20507         _timeoutCount: 0,
20508
20509         /**
20510          * Trying to make the load order less important.  Without this we get
20511          * an error if this file is loaded before the Event Utility.
20512          * @method _addListeners
20513          * @private
20514          * @static
20515          */
20516         _addListeners: function() {
20517             var DDM = Roo.dd.DDM;
20518             if ( Roo.lib.Event && document ) {
20519                 DDM._onLoad();
20520             } else {
20521                 if (DDM._timeoutCount > 2000) {
20522                 } else {
20523                     setTimeout(DDM._addListeners, 10);
20524                     if (document && document.body) {
20525                         DDM._timeoutCount += 1;
20526                     }
20527                 }
20528             }
20529         },
20530
20531         /**
20532          * Recursively searches the immediate parent and all child nodes for
20533          * the handle element in order to determine wheter or not it was
20534          * clicked.
20535          * @method handleWasClicked
20536          * @param node the html element to inspect
20537          * @static
20538          */
20539         handleWasClicked: function(node, id) {
20540             if (this.isHandle(id, node.id)) {
20541                 return true;
20542             } else {
20543                 // check to see if this is a text node child of the one we want
20544                 var p = node.parentNode;
20545
20546                 while (p) {
20547                     if (this.isHandle(id, p.id)) {
20548                         return true;
20549                     } else {
20550                         p = p.parentNode;
20551                     }
20552                 }
20553             }
20554
20555             return false;
20556         }
20557
20558     };
20559
20560 }();
20561
20562 // shorter alias, save a few bytes
20563 Roo.dd.DDM = Roo.dd.DragDropMgr;
20564 Roo.dd.DDM._addListeners();
20565
20566 }/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576
20577 /**
20578  * @class Roo.dd.DD
20579  * A DragDrop implementation where the linked element follows the
20580  * mouse cursor during a drag.
20581  * @extends Roo.dd.DragDrop
20582  * @constructor
20583  * @param {String} id the id of the linked element
20584  * @param {String} sGroup the group of related DragDrop items
20585  * @param {object} config an object containing configurable attributes
20586  *                Valid properties for DD:
20587  *                    scroll
20588  */
20589 Roo.dd.DD = function(id, sGroup, config) {
20590     if (id) {
20591         this.init(id, sGroup, config);
20592     }
20593 };
20594
20595 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20596
20597     /**
20598      * When set to true, the utility automatically tries to scroll the browser
20599      * window wehn a drag and drop element is dragged near the viewport boundary.
20600      * Defaults to true.
20601      * @property scroll
20602      * @type boolean
20603      */
20604     scroll: true,
20605
20606     /**
20607      * Sets the pointer offset to the distance between the linked element's top
20608      * left corner and the location the element was clicked
20609      * @method autoOffset
20610      * @param {int} iPageX the X coordinate of the click
20611      * @param {int} iPageY the Y coordinate of the click
20612      */
20613     autoOffset: function(iPageX, iPageY) {
20614         var x = iPageX - this.startPageX;
20615         var y = iPageY - this.startPageY;
20616         this.setDelta(x, y);
20617     },
20618
20619     /**
20620      * Sets the pointer offset.  You can call this directly to force the
20621      * offset to be in a particular location (e.g., pass in 0,0 to set it
20622      * to the center of the object)
20623      * @method setDelta
20624      * @param {int} iDeltaX the distance from the left
20625      * @param {int} iDeltaY the distance from the top
20626      */
20627     setDelta: function(iDeltaX, iDeltaY) {
20628         this.deltaX = iDeltaX;
20629         this.deltaY = iDeltaY;
20630     },
20631
20632     /**
20633      * Sets the drag element to the location of the mousedown or click event,
20634      * maintaining the cursor location relative to the location on the element
20635      * that was clicked.  Override this if you want to place the element in a
20636      * location other than where the cursor is.
20637      * @method setDragElPos
20638      * @param {int} iPageX the X coordinate of the mousedown or drag event
20639      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20640      */
20641     setDragElPos: function(iPageX, iPageY) {
20642         // the first time we do this, we are going to check to make sure
20643         // the element has css positioning
20644
20645         var el = this.getDragEl();
20646         this.alignElWithMouse(el, iPageX, iPageY);
20647     },
20648
20649     /**
20650      * Sets the element to the location of the mousedown or click event,
20651      * maintaining the cursor location relative to the location on the element
20652      * that was clicked.  Override this if you want to place the element in a
20653      * location other than where the cursor is.
20654      * @method alignElWithMouse
20655      * @param {HTMLElement} el the element to move
20656      * @param {int} iPageX the X coordinate of the mousedown or drag event
20657      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20658      */
20659     alignElWithMouse: function(el, iPageX, iPageY) {
20660         var oCoord = this.getTargetCoord(iPageX, iPageY);
20661         var fly = el.dom ? el : Roo.fly(el);
20662         if (!this.deltaSetXY) {
20663             var aCoord = [oCoord.x, oCoord.y];
20664             fly.setXY(aCoord);
20665             var newLeft = fly.getLeft(true);
20666             var newTop  = fly.getTop(true);
20667             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20668         } else {
20669             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20670         }
20671
20672         this.cachePosition(oCoord.x, oCoord.y);
20673         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20674         return oCoord;
20675     },
20676
20677     /**
20678      * Saves the most recent position so that we can reset the constraints and
20679      * tick marks on-demand.  We need to know this so that we can calculate the
20680      * number of pixels the element is offset from its original position.
20681      * @method cachePosition
20682      * @param iPageX the current x position (optional, this just makes it so we
20683      * don't have to look it up again)
20684      * @param iPageY the current y position (optional, this just makes it so we
20685      * don't have to look it up again)
20686      */
20687     cachePosition: function(iPageX, iPageY) {
20688         if (iPageX) {
20689             this.lastPageX = iPageX;
20690             this.lastPageY = iPageY;
20691         } else {
20692             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20693             this.lastPageX = aCoord[0];
20694             this.lastPageY = aCoord[1];
20695         }
20696     },
20697
20698     /**
20699      * Auto-scroll the window if the dragged object has been moved beyond the
20700      * visible window boundary.
20701      * @method autoScroll
20702      * @param {int} x the drag element's x position
20703      * @param {int} y the drag element's y position
20704      * @param {int} h the height of the drag element
20705      * @param {int} w the width of the drag element
20706      * @private
20707      */
20708     autoScroll: function(x, y, h, w) {
20709
20710         if (this.scroll) {
20711             // The client height
20712             var clientH = Roo.lib.Dom.getViewWidth();
20713
20714             // The client width
20715             var clientW = Roo.lib.Dom.getViewHeight();
20716
20717             // The amt scrolled down
20718             var st = this.DDM.getScrollTop();
20719
20720             // The amt scrolled right
20721             var sl = this.DDM.getScrollLeft();
20722
20723             // Location of the bottom of the element
20724             var bot = h + y;
20725
20726             // Location of the right of the element
20727             var right = w + x;
20728
20729             // The distance from the cursor to the bottom of the visible area,
20730             // adjusted so that we don't scroll if the cursor is beyond the
20731             // element drag constraints
20732             var toBot = (clientH + st - y - this.deltaY);
20733
20734             // The distance from the cursor to the right of the visible area
20735             var toRight = (clientW + sl - x - this.deltaX);
20736
20737
20738             // How close to the edge the cursor must be before we scroll
20739             // var thresh = (document.all) ? 100 : 40;
20740             var thresh = 40;
20741
20742             // How many pixels to scroll per autoscroll op.  This helps to reduce
20743             // clunky scrolling. IE is more sensitive about this ... it needs this
20744             // value to be higher.
20745             var scrAmt = (document.all) ? 80 : 30;
20746
20747             // Scroll down if we are near the bottom of the visible page and the
20748             // obj extends below the crease
20749             if ( bot > clientH && toBot < thresh ) {
20750                 window.scrollTo(sl, st + scrAmt);
20751             }
20752
20753             // Scroll up if the window is scrolled down and the top of the object
20754             // goes above the top border
20755             if ( y < st && st > 0 && y - st < thresh ) {
20756                 window.scrollTo(sl, st - scrAmt);
20757             }
20758
20759             // Scroll right if the obj is beyond the right border and the cursor is
20760             // near the border.
20761             if ( right > clientW && toRight < thresh ) {
20762                 window.scrollTo(sl + scrAmt, st);
20763             }
20764
20765             // Scroll left if the window has been scrolled to the right and the obj
20766             // extends past the left border
20767             if ( x < sl && sl > 0 && x - sl < thresh ) {
20768                 window.scrollTo(sl - scrAmt, st);
20769             }
20770         }
20771     },
20772
20773     /**
20774      * Finds the location the element should be placed if we want to move
20775      * it to where the mouse location less the click offset would place us.
20776      * @method getTargetCoord
20777      * @param {int} iPageX the X coordinate of the click
20778      * @param {int} iPageY the Y coordinate of the click
20779      * @return an object that contains the coordinates (Object.x and Object.y)
20780      * @private
20781      */
20782     getTargetCoord: function(iPageX, iPageY) {
20783
20784
20785         var x = iPageX - this.deltaX;
20786         var y = iPageY - this.deltaY;
20787
20788         if (this.constrainX) {
20789             if (x < this.minX) { x = this.minX; }
20790             if (x > this.maxX) { x = this.maxX; }
20791         }
20792
20793         if (this.constrainY) {
20794             if (y < this.minY) { y = this.minY; }
20795             if (y > this.maxY) { y = this.maxY; }
20796         }
20797
20798         x = this.getTick(x, this.xTicks);
20799         y = this.getTick(y, this.yTicks);
20800
20801
20802         return {x:x, y:y};
20803     },
20804
20805     /*
20806      * Sets up config options specific to this class. Overrides
20807      * Roo.dd.DragDrop, but all versions of this method through the
20808      * inheritance chain are called
20809      */
20810     applyConfig: function() {
20811         Roo.dd.DD.superclass.applyConfig.call(this);
20812         this.scroll = (this.config.scroll !== false);
20813     },
20814
20815     /*
20816      * Event that fires prior to the onMouseDown event.  Overrides
20817      * Roo.dd.DragDrop.
20818      */
20819     b4MouseDown: function(e) {
20820         // this.resetConstraints();
20821         this.autoOffset(e.getPageX(),
20822                             e.getPageY());
20823     },
20824
20825     /*
20826      * Event that fires prior to the onDrag event.  Overrides
20827      * Roo.dd.DragDrop.
20828      */
20829     b4Drag: function(e) {
20830         this.setDragElPos(e.getPageX(),
20831                             e.getPageY());
20832     },
20833
20834     toString: function() {
20835         return ("DD " + this.id);
20836     }
20837
20838     //////////////////////////////////////////////////////////////////////////
20839     // Debugging ygDragDrop events that can be overridden
20840     //////////////////////////////////////////////////////////////////////////
20841     /*
20842     startDrag: function(x, y) {
20843     },
20844
20845     onDrag: function(e) {
20846     },
20847
20848     onDragEnter: function(e, id) {
20849     },
20850
20851     onDragOver: function(e, id) {
20852     },
20853
20854     onDragOut: function(e, id) {
20855     },
20856
20857     onDragDrop: function(e, id) {
20858     },
20859
20860     endDrag: function(e) {
20861     }
20862
20863     */
20864
20865 });/*
20866  * Based on:
20867  * Ext JS Library 1.1.1
20868  * Copyright(c) 2006-2007, Ext JS, LLC.
20869  *
20870  * Originally Released Under LGPL - original licence link has changed is not relivant.
20871  *
20872  * Fork - LGPL
20873  * <script type="text/javascript">
20874  */
20875
20876 /**
20877  * @class Roo.dd.DDProxy
20878  * A DragDrop implementation that inserts an empty, bordered div into
20879  * the document that follows the cursor during drag operations.  At the time of
20880  * the click, the frame div is resized to the dimensions of the linked html
20881  * element, and moved to the exact location of the linked element.
20882  *
20883  * References to the "frame" element refer to the single proxy element that
20884  * was created to be dragged in place of all DDProxy elements on the
20885  * page.
20886  *
20887  * @extends Roo.dd.DD
20888  * @constructor
20889  * @param {String} id the id of the linked html element
20890  * @param {String} sGroup the group of related DragDrop objects
20891  * @param {object} config an object containing configurable attributes
20892  *                Valid properties for DDProxy in addition to those in DragDrop:
20893  *                   resizeFrame, centerFrame, dragElId
20894  */
20895 Roo.dd.DDProxy = function(id, sGroup, config) {
20896     if (id) {
20897         this.init(id, sGroup, config);
20898         this.initFrame();
20899     }
20900 };
20901
20902 /**
20903  * The default drag frame div id
20904  * @property Roo.dd.DDProxy.dragElId
20905  * @type String
20906  * @static
20907  */
20908 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20909
20910 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20911
20912     /**
20913      * By default we resize the drag frame to be the same size as the element
20914      * we want to drag (this is to get the frame effect).  We can turn it off
20915      * if we want a different behavior.
20916      * @property resizeFrame
20917      * @type boolean
20918      */
20919     resizeFrame: true,
20920
20921     /**
20922      * By default the frame is positioned exactly where the drag element is, so
20923      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20924      * you do not have constraints on the obj is to have the drag frame centered
20925      * around the cursor.  Set centerFrame to true for this effect.
20926      * @property centerFrame
20927      * @type boolean
20928      */
20929     centerFrame: false,
20930
20931     /**
20932      * Creates the proxy element if it does not yet exist
20933      * @method createFrame
20934      */
20935     createFrame: function() {
20936         var self = this;
20937         var body = document.body;
20938
20939         if (!body || !body.firstChild) {
20940             setTimeout( function() { self.createFrame(); }, 50 );
20941             return;
20942         }
20943
20944         var div = this.getDragEl();
20945
20946         if (!div) {
20947             div    = document.createElement("div");
20948             div.id = this.dragElId;
20949             var s  = div.style;
20950
20951             s.position   = "absolute";
20952             s.visibility = "hidden";
20953             s.cursor     = "move";
20954             s.border     = "2px solid #aaa";
20955             s.zIndex     = 999;
20956
20957             // appendChild can blow up IE if invoked prior to the window load event
20958             // while rendering a table.  It is possible there are other scenarios
20959             // that would cause this to happen as well.
20960             body.insertBefore(div, body.firstChild);
20961         }
20962     },
20963
20964     /**
20965      * Initialization for the drag frame element.  Must be called in the
20966      * constructor of all subclasses
20967      * @method initFrame
20968      */
20969     initFrame: function() {
20970         this.createFrame();
20971     },
20972
20973     applyConfig: function() {
20974         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20975
20976         this.resizeFrame = (this.config.resizeFrame !== false);
20977         this.centerFrame = (this.config.centerFrame);
20978         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20979     },
20980
20981     /**
20982      * Resizes the drag frame to the dimensions of the clicked object, positions
20983      * it over the object, and finally displays it
20984      * @method showFrame
20985      * @param {int} iPageX X click position
20986      * @param {int} iPageY Y click position
20987      * @private
20988      */
20989     showFrame: function(iPageX, iPageY) {
20990         var el = this.getEl();
20991         var dragEl = this.getDragEl();
20992         var s = dragEl.style;
20993
20994         this._resizeProxy();
20995
20996         if (this.centerFrame) {
20997             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20998                            Math.round(parseInt(s.height, 10)/2) );
20999         }
21000
21001         this.setDragElPos(iPageX, iPageY);
21002
21003         Roo.fly(dragEl).show();
21004     },
21005
21006     /**
21007      * The proxy is automatically resized to the dimensions of the linked
21008      * element when a drag is initiated, unless resizeFrame is set to false
21009      * @method _resizeProxy
21010      * @private
21011      */
21012     _resizeProxy: function() {
21013         if (this.resizeFrame) {
21014             var el = this.getEl();
21015             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21016         }
21017     },
21018
21019     // overrides Roo.dd.DragDrop
21020     b4MouseDown: function(e) {
21021         var x = e.getPageX();
21022         var y = e.getPageY();
21023         this.autoOffset(x, y);
21024         this.setDragElPos(x, y);
21025     },
21026
21027     // overrides Roo.dd.DragDrop
21028     b4StartDrag: function(x, y) {
21029         // show the drag frame
21030         this.showFrame(x, y);
21031     },
21032
21033     // overrides Roo.dd.DragDrop
21034     b4EndDrag: function(e) {
21035         Roo.fly(this.getDragEl()).hide();
21036     },
21037
21038     // overrides Roo.dd.DragDrop
21039     // By default we try to move the element to the last location of the frame.
21040     // This is so that the default behavior mirrors that of Roo.dd.DD.
21041     endDrag: function(e) {
21042
21043         var lel = this.getEl();
21044         var del = this.getDragEl();
21045
21046         // Show the drag frame briefly so we can get its position
21047         del.style.visibility = "";
21048
21049         this.beforeMove();
21050         // Hide the linked element before the move to get around a Safari
21051         // rendering bug.
21052         lel.style.visibility = "hidden";
21053         Roo.dd.DDM.moveToEl(lel, del);
21054         del.style.visibility = "hidden";
21055         lel.style.visibility = "";
21056
21057         this.afterDrag();
21058     },
21059
21060     beforeMove : function(){
21061
21062     },
21063
21064     afterDrag : function(){
21065
21066     },
21067
21068     toString: function() {
21069         return ("DDProxy " + this.id);
21070     }
21071
21072 });
21073 /*
21074  * Based on:
21075  * Ext JS Library 1.1.1
21076  * Copyright(c) 2006-2007, Ext JS, LLC.
21077  *
21078  * Originally Released Under LGPL - original licence link has changed is not relivant.
21079  *
21080  * Fork - LGPL
21081  * <script type="text/javascript">
21082  */
21083
21084  /**
21085  * @class Roo.dd.DDTarget
21086  * A DragDrop implementation that does not move, but can be a drop
21087  * target.  You would get the same result by simply omitting implementation
21088  * for the event callbacks, but this way we reduce the processing cost of the
21089  * event listener and the callbacks.
21090  * @extends Roo.dd.DragDrop
21091  * @constructor
21092  * @param {String} id the id of the element that is a drop target
21093  * @param {String} sGroup the group of related DragDrop objects
21094  * @param {object} config an object containing configurable attributes
21095  *                 Valid properties for DDTarget in addition to those in
21096  *                 DragDrop:
21097  *                    none
21098  */
21099 Roo.dd.DDTarget = function(id, sGroup, config) {
21100     if (id) {
21101         this.initTarget(id, sGroup, config);
21102     }
21103     if (config && (config.listeners || config.events)) { 
21104         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21105             listeners : config.listeners || {}, 
21106             events : config.events || {} 
21107         });    
21108     }
21109 };
21110
21111 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21112 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21113     toString: function() {
21114         return ("DDTarget " + this.id);
21115     }
21116 });
21117 /*
21118  * Based on:
21119  * Ext JS Library 1.1.1
21120  * Copyright(c) 2006-2007, Ext JS, LLC.
21121  *
21122  * Originally Released Under LGPL - original licence link has changed is not relivant.
21123  *
21124  * Fork - LGPL
21125  * <script type="text/javascript">
21126  */
21127  
21128
21129 /**
21130  * @class Roo.dd.ScrollManager
21131  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21132  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21133  * @singleton
21134  */
21135 Roo.dd.ScrollManager = function(){
21136     var ddm = Roo.dd.DragDropMgr;
21137     var els = {};
21138     var dragEl = null;
21139     var proc = {};
21140     
21141     
21142     
21143     var onStop = function(e){
21144         dragEl = null;
21145         clearProc();
21146     };
21147     
21148     var triggerRefresh = function(){
21149         if(ddm.dragCurrent){
21150              ddm.refreshCache(ddm.dragCurrent.groups);
21151         }
21152     };
21153     
21154     var doScroll = function(){
21155         if(ddm.dragCurrent){
21156             var dds = Roo.dd.ScrollManager;
21157             if(!dds.animate){
21158                 if(proc.el.scroll(proc.dir, dds.increment)){
21159                     triggerRefresh();
21160                 }
21161             }else{
21162                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21163             }
21164         }
21165     };
21166     
21167     var clearProc = function(){
21168         if(proc.id){
21169             clearInterval(proc.id);
21170         }
21171         proc.id = 0;
21172         proc.el = null;
21173         proc.dir = "";
21174     };
21175     
21176     var startProc = function(el, dir){
21177          Roo.log('scroll startproc');
21178         clearProc();
21179         proc.el = el;
21180         proc.dir = dir;
21181         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21182     };
21183     
21184     var onFire = function(e, isDrop){
21185        
21186         if(isDrop || !ddm.dragCurrent){ return; }
21187         var dds = Roo.dd.ScrollManager;
21188         if(!dragEl || dragEl != ddm.dragCurrent){
21189             dragEl = ddm.dragCurrent;
21190             // refresh regions on drag start
21191             dds.refreshCache();
21192         }
21193         
21194         var xy = Roo.lib.Event.getXY(e);
21195         var pt = new Roo.lib.Point(xy[0], xy[1]);
21196         for(var id in els){
21197             var el = els[id], r = el._region;
21198             if(r && r.contains(pt) && el.isScrollable()){
21199                 if(r.bottom - pt.y <= dds.thresh){
21200                     if(proc.el != el){
21201                         startProc(el, "down");
21202                     }
21203                     return;
21204                 }else if(r.right - pt.x <= dds.thresh){
21205                     if(proc.el != el){
21206                         startProc(el, "left");
21207                     }
21208                     return;
21209                 }else if(pt.y - r.top <= dds.thresh){
21210                     if(proc.el != el){
21211                         startProc(el, "up");
21212                     }
21213                     return;
21214                 }else if(pt.x - r.left <= dds.thresh){
21215                     if(proc.el != el){
21216                         startProc(el, "right");
21217                     }
21218                     return;
21219                 }
21220             }
21221         }
21222         clearProc();
21223     };
21224     
21225     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21226     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21227     
21228     return {
21229         /**
21230          * Registers new overflow element(s) to auto scroll
21231          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21232          */
21233         register : function(el){
21234             if(el instanceof Array){
21235                 for(var i = 0, len = el.length; i < len; i++) {
21236                         this.register(el[i]);
21237                 }
21238             }else{
21239                 el = Roo.get(el);
21240                 els[el.id] = el;
21241             }
21242             Roo.dd.ScrollManager.els = els;
21243         },
21244         
21245         /**
21246          * Unregisters overflow element(s) so they are no longer scrolled
21247          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21248          */
21249         unregister : function(el){
21250             if(el instanceof Array){
21251                 for(var i = 0, len = el.length; i < len; i++) {
21252                         this.unregister(el[i]);
21253                 }
21254             }else{
21255                 el = Roo.get(el);
21256                 delete els[el.id];
21257             }
21258         },
21259         
21260         /**
21261          * The number of pixels from the edge of a container the pointer needs to be to 
21262          * trigger scrolling (defaults to 25)
21263          * @type Number
21264          */
21265         thresh : 25,
21266         
21267         /**
21268          * The number of pixels to scroll in each scroll increment (defaults to 50)
21269          * @type Number
21270          */
21271         increment : 100,
21272         
21273         /**
21274          * The frequency of scrolls in milliseconds (defaults to 500)
21275          * @type Number
21276          */
21277         frequency : 500,
21278         
21279         /**
21280          * True to animate the scroll (defaults to true)
21281          * @type Boolean
21282          */
21283         animate: true,
21284         
21285         /**
21286          * The animation duration in seconds - 
21287          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21288          * @type Number
21289          */
21290         animDuration: .4,
21291         
21292         /**
21293          * Manually trigger a cache refresh.
21294          */
21295         refreshCache : function(){
21296             for(var id in els){
21297                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21298                     els[id]._region = els[id].getRegion();
21299                 }
21300             }
21301         }
21302     };
21303 }();/*
21304  * Based on:
21305  * Ext JS Library 1.1.1
21306  * Copyright(c) 2006-2007, Ext JS, LLC.
21307  *
21308  * Originally Released Under LGPL - original licence link has changed is not relivant.
21309  *
21310  * Fork - LGPL
21311  * <script type="text/javascript">
21312  */
21313  
21314
21315 /**
21316  * @class Roo.dd.Registry
21317  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21318  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21319  * @singleton
21320  */
21321 Roo.dd.Registry = function(){
21322     var elements = {}; 
21323     var handles = {}; 
21324     var autoIdSeed = 0;
21325
21326     var getId = function(el, autogen){
21327         if(typeof el == "string"){
21328             return el;
21329         }
21330         var id = el.id;
21331         if(!id && autogen !== false){
21332             id = "roodd-" + (++autoIdSeed);
21333             el.id = id;
21334         }
21335         return id;
21336     };
21337     
21338     return {
21339     /**
21340      * Register a drag drop element
21341      * @param {String|HTMLElement} element The id or DOM node to register
21342      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21343      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21344      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21345      * populated in the data object (if applicable):
21346      * <pre>
21347 Value      Description<br />
21348 ---------  ------------------------------------------<br />
21349 handles    Array of DOM nodes that trigger dragging<br />
21350            for the element being registered<br />
21351 isHandle   True if the element passed in triggers<br />
21352            dragging itself, else false
21353 </pre>
21354      */
21355         register : function(el, data){
21356             data = data || {};
21357             if(typeof el == "string"){
21358                 el = document.getElementById(el);
21359             }
21360             data.ddel = el;
21361             elements[getId(el)] = data;
21362             if(data.isHandle !== false){
21363                 handles[data.ddel.id] = data;
21364             }
21365             if(data.handles){
21366                 var hs = data.handles;
21367                 for(var i = 0, len = hs.length; i < len; i++){
21368                         handles[getId(hs[i])] = data;
21369                 }
21370             }
21371         },
21372
21373     /**
21374      * Unregister a drag drop element
21375      * @param {String|HTMLElement}  element The id or DOM node to unregister
21376      */
21377         unregister : function(el){
21378             var id = getId(el, false);
21379             var data = elements[id];
21380             if(data){
21381                 delete elements[id];
21382                 if(data.handles){
21383                     var hs = data.handles;
21384                     for(var i = 0, len = hs.length; i < len; i++){
21385                         delete handles[getId(hs[i], false)];
21386                     }
21387                 }
21388             }
21389         },
21390
21391     /**
21392      * Returns the handle registered for a DOM Node by id
21393      * @param {String|HTMLElement} id The DOM node or id to look up
21394      * @return {Object} handle The custom handle data
21395      */
21396         getHandle : function(id){
21397             if(typeof id != "string"){ // must be element?
21398                 id = id.id;
21399             }
21400             return handles[id];
21401         },
21402
21403     /**
21404      * Returns the handle that is registered for the DOM node that is the target of the event
21405      * @param {Event} e The event
21406      * @return {Object} handle The custom handle data
21407      */
21408         getHandleFromEvent : function(e){
21409             var t = Roo.lib.Event.getTarget(e);
21410             return t ? handles[t.id] : null;
21411         },
21412
21413     /**
21414      * Returns a custom data object that is registered for a DOM node by id
21415      * @param {String|HTMLElement} id The DOM node or id to look up
21416      * @return {Object} data The custom data
21417      */
21418         getTarget : function(id){
21419             if(typeof id != "string"){ // must be element?
21420                 id = id.id;
21421             }
21422             return elements[id];
21423         },
21424
21425     /**
21426      * Returns a custom data object that is registered for the DOM node that is the target of the event
21427      * @param {Event} e The event
21428      * @return {Object} data The custom data
21429      */
21430         getTargetFromEvent : function(e){
21431             var t = Roo.lib.Event.getTarget(e);
21432             return t ? elements[t.id] || handles[t.id] : null;
21433         }
21434     };
21435 }();/*
21436  * Based on:
21437  * Ext JS Library 1.1.1
21438  * Copyright(c) 2006-2007, Ext JS, LLC.
21439  *
21440  * Originally Released Under LGPL - original licence link has changed is not relivant.
21441  *
21442  * Fork - LGPL
21443  * <script type="text/javascript">
21444  */
21445  
21446
21447 /**
21448  * @class Roo.dd.StatusProxy
21449  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21450  * default drag proxy used by all Roo.dd components.
21451  * @constructor
21452  * @param {Object} config
21453  */
21454 Roo.dd.StatusProxy = function(config){
21455     Roo.apply(this, config);
21456     this.id = this.id || Roo.id();
21457     this.el = new Roo.Layer({
21458         dh: {
21459             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21460                 {tag: "div", cls: "x-dd-drop-icon"},
21461                 {tag: "div", cls: "x-dd-drag-ghost"}
21462             ]
21463         }, 
21464         shadow: !config || config.shadow !== false
21465     });
21466     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21467     this.dropStatus = this.dropNotAllowed;
21468 };
21469
21470 Roo.dd.StatusProxy.prototype = {
21471     /**
21472      * @cfg {String} dropAllowed
21473      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21474      */
21475     dropAllowed : "x-dd-drop-ok",
21476     /**
21477      * @cfg {String} dropNotAllowed
21478      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21479      */
21480     dropNotAllowed : "x-dd-drop-nodrop",
21481
21482     /**
21483      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21484      * over the current target element.
21485      * @param {String} cssClass The css class for the new drop status indicator image
21486      */
21487     setStatus : function(cssClass){
21488         cssClass = cssClass || this.dropNotAllowed;
21489         if(this.dropStatus != cssClass){
21490             this.el.replaceClass(this.dropStatus, cssClass);
21491             this.dropStatus = cssClass;
21492         }
21493     },
21494
21495     /**
21496      * Resets the status indicator to the default dropNotAllowed value
21497      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21498      */
21499     reset : function(clearGhost){
21500         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21501         this.dropStatus = this.dropNotAllowed;
21502         if(clearGhost){
21503             this.ghost.update("");
21504         }
21505     },
21506
21507     /**
21508      * Updates the contents of the ghost element
21509      * @param {String} html The html that will replace the current innerHTML of the ghost element
21510      */
21511     update : function(html){
21512         if(typeof html == "string"){
21513             this.ghost.update(html);
21514         }else{
21515             this.ghost.update("");
21516             html.style.margin = "0";
21517             this.ghost.dom.appendChild(html);
21518         }
21519         // ensure float = none set?? cant remember why though.
21520         var el = this.ghost.dom.firstChild;
21521                 if(el){
21522                         Roo.fly(el).setStyle('float', 'none');
21523                 }
21524     },
21525     
21526     /**
21527      * Returns the underlying proxy {@link Roo.Layer}
21528      * @return {Roo.Layer} el
21529     */
21530     getEl : function(){
21531         return this.el;
21532     },
21533
21534     /**
21535      * Returns the ghost element
21536      * @return {Roo.Element} el
21537      */
21538     getGhost : function(){
21539         return this.ghost;
21540     },
21541
21542     /**
21543      * Hides the proxy
21544      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21545      */
21546     hide : function(clear){
21547         this.el.hide();
21548         if(clear){
21549             this.reset(true);
21550         }
21551     },
21552
21553     /**
21554      * Stops the repair animation if it's currently running
21555      */
21556     stop : function(){
21557         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21558             this.anim.stop();
21559         }
21560     },
21561
21562     /**
21563      * Displays this proxy
21564      */
21565     show : function(){
21566         this.el.show();
21567     },
21568
21569     /**
21570      * Force the Layer to sync its shadow and shim positions to the element
21571      */
21572     sync : function(){
21573         this.el.sync();
21574     },
21575
21576     /**
21577      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21578      * invalid drop operation by the item being dragged.
21579      * @param {Array} xy The XY position of the element ([x, y])
21580      * @param {Function} callback The function to call after the repair is complete
21581      * @param {Object} scope The scope in which to execute the callback
21582      */
21583     repair : function(xy, callback, scope){
21584         this.callback = callback;
21585         this.scope = scope;
21586         if(xy && this.animRepair !== false){
21587             this.el.addClass("x-dd-drag-repair");
21588             this.el.hideUnders(true);
21589             this.anim = this.el.shift({
21590                 duration: this.repairDuration || .5,
21591                 easing: 'easeOut',
21592                 xy: xy,
21593                 stopFx: true,
21594                 callback: this.afterRepair,
21595                 scope: this
21596             });
21597         }else{
21598             this.afterRepair();
21599         }
21600     },
21601
21602     // private
21603     afterRepair : function(){
21604         this.hide(true);
21605         if(typeof this.callback == "function"){
21606             this.callback.call(this.scope || this);
21607         }
21608         this.callback = null;
21609         this.scope = null;
21610     }
21611 };/*
21612  * Based on:
21613  * Ext JS Library 1.1.1
21614  * Copyright(c) 2006-2007, Ext JS, LLC.
21615  *
21616  * Originally Released Under LGPL - original licence link has changed is not relivant.
21617  *
21618  * Fork - LGPL
21619  * <script type="text/javascript">
21620  */
21621
21622 /**
21623  * @class Roo.dd.DragSource
21624  * @extends Roo.dd.DDProxy
21625  * A simple class that provides the basic implementation needed to make any element draggable.
21626  * @constructor
21627  * @param {String/HTMLElement/Element} el The container element
21628  * @param {Object} config
21629  */
21630 Roo.dd.DragSource = function(el, config){
21631     this.el = Roo.get(el);
21632     this.dragData = {};
21633     
21634     Roo.apply(this, config);
21635     
21636     if(!this.proxy){
21637         this.proxy = new Roo.dd.StatusProxy();
21638     }
21639
21640     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21641           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21642     
21643     this.dragging = false;
21644 };
21645
21646 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21647     /**
21648      * @cfg {String} dropAllowed
21649      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21650      */
21651     dropAllowed : "x-dd-drop-ok",
21652     /**
21653      * @cfg {String} dropNotAllowed
21654      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21655      */
21656     dropNotAllowed : "x-dd-drop-nodrop",
21657
21658     /**
21659      * Returns the data object associated with this drag source
21660      * @return {Object} data An object containing arbitrary data
21661      */
21662     getDragData : function(e){
21663         return this.dragData;
21664     },
21665
21666     // private
21667     onDragEnter : function(e, id){
21668         var target = Roo.dd.DragDropMgr.getDDById(id);
21669         this.cachedTarget = target;
21670         if(this.beforeDragEnter(target, e, id) !== false){
21671             if(target.isNotifyTarget){
21672                 var status = target.notifyEnter(this, e, this.dragData);
21673                 this.proxy.setStatus(status);
21674             }else{
21675                 this.proxy.setStatus(this.dropAllowed);
21676             }
21677             
21678             if(this.afterDragEnter){
21679                 /**
21680                  * An empty function by default, but provided so that you can perform a custom action
21681                  * when the dragged item enters the drop target by providing an implementation.
21682                  * @param {Roo.dd.DragDrop} target The drop target
21683                  * @param {Event} e The event object
21684                  * @param {String} id The id of the dragged element
21685                  * @method afterDragEnter
21686                  */
21687                 this.afterDragEnter(target, e, id);
21688             }
21689         }
21690     },
21691
21692     /**
21693      * An empty function by default, but provided so that you can perform a custom action
21694      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21695      * @param {Roo.dd.DragDrop} target The drop target
21696      * @param {Event} e The event object
21697      * @param {String} id The id of the dragged element
21698      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21699      */
21700     beforeDragEnter : function(target, e, id){
21701         return true;
21702     },
21703
21704     // private
21705     alignElWithMouse: function() {
21706         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21707         this.proxy.sync();
21708     },
21709
21710     // private
21711     onDragOver : function(e, id){
21712         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21713         if(this.beforeDragOver(target, e, id) !== false){
21714             if(target.isNotifyTarget){
21715                 var status = target.notifyOver(this, e, this.dragData);
21716                 this.proxy.setStatus(status);
21717             }
21718
21719             if(this.afterDragOver){
21720                 /**
21721                  * An empty function by default, but provided so that you can perform a custom action
21722                  * while the dragged item is over the drop target by providing an implementation.
21723                  * @param {Roo.dd.DragDrop} target The drop target
21724                  * @param {Event} e The event object
21725                  * @param {String} id The id of the dragged element
21726                  * @method afterDragOver
21727                  */
21728                 this.afterDragOver(target, e, id);
21729             }
21730         }
21731     },
21732
21733     /**
21734      * An empty function by default, but provided so that you can perform a custom action
21735      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21736      * @param {Roo.dd.DragDrop} target The drop target
21737      * @param {Event} e The event object
21738      * @param {String} id The id of the dragged element
21739      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21740      */
21741     beforeDragOver : function(target, e, id){
21742         return true;
21743     },
21744
21745     // private
21746     onDragOut : function(e, id){
21747         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21748         if(this.beforeDragOut(target, e, id) !== false){
21749             if(target.isNotifyTarget){
21750                 target.notifyOut(this, e, this.dragData);
21751             }
21752             this.proxy.reset();
21753             if(this.afterDragOut){
21754                 /**
21755                  * An empty function by default, but provided so that you can perform a custom action
21756                  * after the dragged item is dragged out of the target without dropping.
21757                  * @param {Roo.dd.DragDrop} target The drop target
21758                  * @param {Event} e The event object
21759                  * @param {String} id The id of the dragged element
21760                  * @method afterDragOut
21761                  */
21762                 this.afterDragOut(target, e, id);
21763             }
21764         }
21765         this.cachedTarget = null;
21766     },
21767
21768     /**
21769      * An empty function by default, but provided so that you can perform a custom action before the dragged
21770      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21771      * @param {Roo.dd.DragDrop} target The drop target
21772      * @param {Event} e The event object
21773      * @param {String} id The id of the dragged element
21774      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21775      */
21776     beforeDragOut : function(target, e, id){
21777         return true;
21778     },
21779     
21780     // private
21781     onDragDrop : function(e, id){
21782         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21783         if(this.beforeDragDrop(target, e, id) !== false){
21784             if(target.isNotifyTarget){
21785                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21786                     this.onValidDrop(target, e, id);
21787                 }else{
21788                     this.onInvalidDrop(target, e, id);
21789                 }
21790             }else{
21791                 this.onValidDrop(target, e, id);
21792             }
21793             
21794             if(this.afterDragDrop){
21795                 /**
21796                  * An empty function by default, but provided so that you can perform a custom action
21797                  * after a valid drag drop has occurred by providing an implementation.
21798                  * @param {Roo.dd.DragDrop} target The drop target
21799                  * @param {Event} e The event object
21800                  * @param {String} id The id of the dropped element
21801                  * @method afterDragDrop
21802                  */
21803                 this.afterDragDrop(target, e, id);
21804             }
21805         }
21806         delete this.cachedTarget;
21807     },
21808
21809     /**
21810      * An empty function by default, but provided so that you can perform a custom action before the dragged
21811      * item is dropped onto the target and optionally cancel the onDragDrop.
21812      * @param {Roo.dd.DragDrop} target The drop target
21813      * @param {Event} e The event object
21814      * @param {String} id The id of the dragged element
21815      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21816      */
21817     beforeDragDrop : function(target, e, id){
21818         return true;
21819     },
21820
21821     // private
21822     onValidDrop : function(target, e, id){
21823         this.hideProxy();
21824         if(this.afterValidDrop){
21825             /**
21826              * An empty function by default, but provided so that you can perform a custom action
21827              * after a valid drop has occurred by providing an implementation.
21828              * @param {Object} target The target DD 
21829              * @param {Event} e The event object
21830              * @param {String} id The id of the dropped element
21831              * @method afterInvalidDrop
21832              */
21833             this.afterValidDrop(target, e, id);
21834         }
21835     },
21836
21837     // private
21838     getRepairXY : function(e, data){
21839         return this.el.getXY();  
21840     },
21841
21842     // private
21843     onInvalidDrop : function(target, e, id){
21844         this.beforeInvalidDrop(target, e, id);
21845         if(this.cachedTarget){
21846             if(this.cachedTarget.isNotifyTarget){
21847                 this.cachedTarget.notifyOut(this, e, this.dragData);
21848             }
21849             this.cacheTarget = null;
21850         }
21851         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21852
21853         if(this.afterInvalidDrop){
21854             /**
21855              * An empty function by default, but provided so that you can perform a custom action
21856              * after an invalid drop has occurred by providing an implementation.
21857              * @param {Event} e The event object
21858              * @param {String} id The id of the dropped element
21859              * @method afterInvalidDrop
21860              */
21861             this.afterInvalidDrop(e, id);
21862         }
21863     },
21864
21865     // private
21866     afterRepair : function(){
21867         if(Roo.enableFx){
21868             this.el.highlight(this.hlColor || "c3daf9");
21869         }
21870         this.dragging = false;
21871     },
21872
21873     /**
21874      * An empty function by default, but provided so that you can perform a custom action after an invalid
21875      * drop has occurred.
21876      * @param {Roo.dd.DragDrop} target The drop target
21877      * @param {Event} e The event object
21878      * @param {String} id The id of the dragged element
21879      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21880      */
21881     beforeInvalidDrop : function(target, e, id){
21882         return true;
21883     },
21884
21885     // private
21886     handleMouseDown : function(e){
21887         if(this.dragging) {
21888             return;
21889         }
21890         var data = this.getDragData(e);
21891         if(data && this.onBeforeDrag(data, e) !== false){
21892             this.dragData = data;
21893             this.proxy.stop();
21894             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21895         } 
21896     },
21897
21898     /**
21899      * An empty function by default, but provided so that you can perform a custom action before the initial
21900      * drag event begins and optionally cancel it.
21901      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21902      * @param {Event} e The event object
21903      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21904      */
21905     onBeforeDrag : function(data, e){
21906         return true;
21907     },
21908
21909     /**
21910      * An empty function by default, but provided so that you can perform a custom action once the initial
21911      * drag event has begun.  The drag cannot be canceled from this function.
21912      * @param {Number} x The x position of the click on the dragged object
21913      * @param {Number} y The y position of the click on the dragged object
21914      */
21915     onStartDrag : Roo.emptyFn,
21916
21917     // private - YUI override
21918     startDrag : function(x, y){
21919         this.proxy.reset();
21920         this.dragging = true;
21921         this.proxy.update("");
21922         this.onInitDrag(x, y);
21923         this.proxy.show();
21924     },
21925
21926     // private
21927     onInitDrag : function(x, y){
21928         var clone = this.el.dom.cloneNode(true);
21929         clone.id = Roo.id(); // prevent duplicate ids
21930         this.proxy.update(clone);
21931         this.onStartDrag(x, y);
21932         return true;
21933     },
21934
21935     /**
21936      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21937      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21938      */
21939     getProxy : function(){
21940         return this.proxy;  
21941     },
21942
21943     /**
21944      * Hides the drag source's {@link Roo.dd.StatusProxy}
21945      */
21946     hideProxy : function(){
21947         this.proxy.hide();  
21948         this.proxy.reset(true);
21949         this.dragging = false;
21950     },
21951
21952     // private
21953     triggerCacheRefresh : function(){
21954         Roo.dd.DDM.refreshCache(this.groups);
21955     },
21956
21957     // private - override to prevent hiding
21958     b4EndDrag: function(e) {
21959     },
21960
21961     // private - override to prevent moving
21962     endDrag : function(e){
21963         this.onEndDrag(this.dragData, e);
21964     },
21965
21966     // private
21967     onEndDrag : function(data, e){
21968     },
21969     
21970     // private - pin to cursor
21971     autoOffset : function(x, y) {
21972         this.setDelta(-12, -20);
21973     }    
21974 });/*
21975  * Based on:
21976  * Ext JS Library 1.1.1
21977  * Copyright(c) 2006-2007, Ext JS, LLC.
21978  *
21979  * Originally Released Under LGPL - original licence link has changed is not relivant.
21980  *
21981  * Fork - LGPL
21982  * <script type="text/javascript">
21983  */
21984
21985
21986 /**
21987  * @class Roo.dd.DropTarget
21988  * @extends Roo.dd.DDTarget
21989  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21990  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21991  * @constructor
21992  * @param {String/HTMLElement/Element} el The container element
21993  * @param {Object} config
21994  */
21995 Roo.dd.DropTarget = function(el, config){
21996     this.el = Roo.get(el);
21997     
21998     var listeners = false; ;
21999     if (config && config.listeners) {
22000         listeners= config.listeners;
22001         delete config.listeners;
22002     }
22003     Roo.apply(this, config);
22004     
22005     if(this.containerScroll){
22006         Roo.dd.ScrollManager.register(this.el);
22007     }
22008     this.addEvents( {
22009          /**
22010          * @scope Roo.dd.DropTarget
22011          */
22012          
22013          /**
22014          * @event enter
22015          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22016          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22017          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22018          * 
22019          * IMPORTANT : it should set this.overClass and this.dropAllowed
22020          * 
22021          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22022          * @param {Event} e The event
22023          * @param {Object} data An object containing arbitrary data supplied by the drag source
22024          */
22025         "enter" : true,
22026         
22027          /**
22028          * @event over
22029          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22030          * This method will be called on every mouse movement while the drag source is over the drop target.
22031          * This default implementation simply returns the dropAllowed config value.
22032          * 
22033          * IMPORTANT : it should set this.dropAllowed
22034          * 
22035          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22036          * @param {Event} e The event
22037          * @param {Object} data An object containing arbitrary data supplied by the drag source
22038          
22039          */
22040         "over" : true,
22041         /**
22042          * @event out
22043          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22044          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22045          * overClass (if any) from the drop element.
22046          * 
22047          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22048          * @param {Event} e The event
22049          * @param {Object} data An object containing arbitrary data supplied by the drag source
22050          */
22051          "out" : true,
22052          
22053         /**
22054          * @event drop
22055          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22056          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22057          * implementation that does something to process the drop event and returns true so that the drag source's
22058          * repair action does not run.
22059          * 
22060          * IMPORTANT : it should set this.success
22061          * 
22062          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22063          * @param {Event} e The event
22064          * @param {Object} data An object containing arbitrary data supplied by the drag source
22065         */
22066          "drop" : true
22067     });
22068             
22069      
22070     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22071         this.el.dom, 
22072         this.ddGroup || this.group,
22073         {
22074             isTarget: true,
22075             listeners : listeners || {} 
22076            
22077         
22078         }
22079     );
22080
22081 };
22082
22083 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22084     /**
22085      * @cfg {String} overClass
22086      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22087      */
22088      /**
22089      * @cfg {String} ddGroup
22090      * The drag drop group to handle drop events for
22091      */
22092      
22093     /**
22094      * @cfg {String} dropAllowed
22095      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22096      */
22097     dropAllowed : "x-dd-drop-ok",
22098     /**
22099      * @cfg {String} dropNotAllowed
22100      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22101      */
22102     dropNotAllowed : "x-dd-drop-nodrop",
22103     /**
22104      * @cfg {boolean} success
22105      * set this after drop listener.. 
22106      */
22107     success : false,
22108     /**
22109      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22110      * if the drop point is valid for over/enter..
22111      */
22112     valid : false,
22113     // private
22114     isTarget : true,
22115
22116     // private
22117     isNotifyTarget : true,
22118     
22119     /**
22120      * @hide
22121      */
22122     notifyEnter : function(dd, e, data)
22123     {
22124         this.valid = true;
22125         this.fireEvent('enter', dd, e, data);
22126         if(this.overClass){
22127             this.el.addClass(this.overClass);
22128         }
22129         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22130             this.valid ? this.dropAllowed : this.dropNotAllowed
22131         );
22132     },
22133
22134     /**
22135      * @hide
22136      */
22137     notifyOver : function(dd, e, data)
22138     {
22139         this.valid = true;
22140         this.fireEvent('over', dd, e, data);
22141         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22142             this.valid ? this.dropAllowed : this.dropNotAllowed
22143         );
22144     },
22145
22146     /**
22147      * @hide
22148      */
22149     notifyOut : function(dd, e, data)
22150     {
22151         this.fireEvent('out', dd, e, data);
22152         if(this.overClass){
22153             this.el.removeClass(this.overClass);
22154         }
22155     },
22156
22157     /**
22158      * @hide
22159      */
22160     notifyDrop : function(dd, e, data)
22161     {
22162         this.success = false;
22163         this.fireEvent('drop', dd, e, data);
22164         return this.success;
22165     }
22166 });/*
22167  * Based on:
22168  * Ext JS Library 1.1.1
22169  * Copyright(c) 2006-2007, Ext JS, LLC.
22170  *
22171  * Originally Released Under LGPL - original licence link has changed is not relivant.
22172  *
22173  * Fork - LGPL
22174  * <script type="text/javascript">
22175  */
22176
22177
22178 /**
22179  * @class Roo.dd.DragZone
22180  * @extends Roo.dd.DragSource
22181  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22182  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22183  * @constructor
22184  * @param {String/HTMLElement/Element} el The container element
22185  * @param {Object} config
22186  */
22187 Roo.dd.DragZone = function(el, config){
22188     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22189     if(this.containerScroll){
22190         Roo.dd.ScrollManager.register(this.el);
22191     }
22192 };
22193
22194 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22195     /**
22196      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22197      * for auto scrolling during drag operations.
22198      */
22199     /**
22200      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22201      * method after a failed drop (defaults to "c3daf9" - light blue)
22202      */
22203
22204     /**
22205      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22206      * for a valid target to drag based on the mouse down. Override this method
22207      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22208      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22209      * @param {EventObject} e The mouse down event
22210      * @return {Object} The dragData
22211      */
22212     getDragData : function(e){
22213         return Roo.dd.Registry.getHandleFromEvent(e);
22214     },
22215     
22216     /**
22217      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22218      * this.dragData.ddel
22219      * @param {Number} x The x position of the click on the dragged object
22220      * @param {Number} y The y position of the click on the dragged object
22221      * @return {Boolean} true to continue the drag, false to cancel
22222      */
22223     onInitDrag : function(x, y){
22224         this.proxy.update(this.dragData.ddel.cloneNode(true));
22225         this.onStartDrag(x, y);
22226         return true;
22227     },
22228     
22229     /**
22230      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22231      */
22232     afterRepair : function(){
22233         if(Roo.enableFx){
22234             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22235         }
22236         this.dragging = false;
22237     },
22238
22239     /**
22240      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22241      * the XY of this.dragData.ddel
22242      * @param {EventObject} e The mouse up event
22243      * @return {Array} The xy location (e.g. [100, 200])
22244      */
22245     getRepairXY : function(e){
22246         return Roo.Element.fly(this.dragData.ddel).getXY();  
22247     }
22248 });/*
22249  * Based on:
22250  * Ext JS Library 1.1.1
22251  * Copyright(c) 2006-2007, Ext JS, LLC.
22252  *
22253  * Originally Released Under LGPL - original licence link has changed is not relivant.
22254  *
22255  * Fork - LGPL
22256  * <script type="text/javascript">
22257  */
22258 /**
22259  * @class Roo.dd.DropZone
22260  * @extends Roo.dd.DropTarget
22261  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22262  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22263  * @constructor
22264  * @param {String/HTMLElement/Element} el The container element
22265  * @param {Object} config
22266  */
22267 Roo.dd.DropZone = function(el, config){
22268     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22269 };
22270
22271 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22272     /**
22273      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22274      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22275      * provide your own custom lookup.
22276      * @param {Event} e The event
22277      * @return {Object} data The custom data
22278      */
22279     getTargetFromEvent : function(e){
22280         return Roo.dd.Registry.getTargetFromEvent(e);
22281     },
22282
22283     /**
22284      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22285      * that it has registered.  This method has no default implementation and should be overridden to provide
22286      * node-specific processing if necessary.
22287      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22288      * {@link #getTargetFromEvent} for this node)
22289      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22290      * @param {Event} e The event
22291      * @param {Object} data An object containing arbitrary data supplied by the drag source
22292      */
22293     onNodeEnter : function(n, dd, e, data){
22294         
22295     },
22296
22297     /**
22298      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22299      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22300      * overridden to provide the proper feedback.
22301      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22302      * {@link #getTargetFromEvent} for this node)
22303      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22304      * @param {Event} e The event
22305      * @param {Object} data An object containing arbitrary data supplied by the drag source
22306      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22307      * underlying {@link Roo.dd.StatusProxy} can be updated
22308      */
22309     onNodeOver : function(n, dd, e, data){
22310         return this.dropAllowed;
22311     },
22312
22313     /**
22314      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22315      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22316      * node-specific processing if necessary.
22317      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22318      * {@link #getTargetFromEvent} for this node)
22319      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22320      * @param {Event} e The event
22321      * @param {Object} data An object containing arbitrary data supplied by the drag source
22322      */
22323     onNodeOut : function(n, dd, e, data){
22324         
22325     },
22326
22327     /**
22328      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22329      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22330      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22331      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22332      * {@link #getTargetFromEvent} for this node)
22333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22334      * @param {Event} e The event
22335      * @param {Object} data An object containing arbitrary data supplied by the drag source
22336      * @return {Boolean} True if the drop was valid, else false
22337      */
22338     onNodeDrop : function(n, dd, e, data){
22339         return false;
22340     },
22341
22342     /**
22343      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22344      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22345      * it should be overridden to provide the proper feedback if necessary.
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     onContainerOver : function(dd, e, data){
22353         return this.dropNotAllowed;
22354     },
22355
22356     /**
22357      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22358      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22359      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22360      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22361      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22362      * @param {Event} e The event
22363      * @param {Object} data An object containing arbitrary data supplied by the drag source
22364      * @return {Boolean} True if the drop was valid, else false
22365      */
22366     onContainerDrop : function(dd, e, data){
22367         return false;
22368     },
22369
22370     /**
22371      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22372      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22373      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22374      * you should override this method and provide a custom implementation.
22375      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22376      * @param {Event} e The event
22377      * @param {Object} data An object containing arbitrary data supplied by the drag source
22378      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22379      * underlying {@link Roo.dd.StatusProxy} can be updated
22380      */
22381     notifyEnter : function(dd, e, data){
22382         return this.dropNotAllowed;
22383     },
22384
22385     /**
22386      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22387      * This method will be called on every mouse movement while the drag source is over the drop zone.
22388      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22389      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22390      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22391      * registered node, it will call {@link #onContainerOver}.
22392      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22393      * @param {Event} e The event
22394      * @param {Object} data An object containing arbitrary data supplied by the drag source
22395      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22396      * underlying {@link Roo.dd.StatusProxy} can be updated
22397      */
22398     notifyOver : function(dd, e, data){
22399         var n = this.getTargetFromEvent(e);
22400         if(!n){ // not over valid drop target
22401             if(this.lastOverNode){
22402                 this.onNodeOut(this.lastOverNode, dd, e, data);
22403                 this.lastOverNode = null;
22404             }
22405             return this.onContainerOver(dd, e, data);
22406         }
22407         if(this.lastOverNode != n){
22408             if(this.lastOverNode){
22409                 this.onNodeOut(this.lastOverNode, dd, e, data);
22410             }
22411             this.onNodeEnter(n, dd, e, data);
22412             this.lastOverNode = n;
22413         }
22414         return this.onNodeOver(n, dd, e, data);
22415     },
22416
22417     /**
22418      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22419      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22420      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22422      * @param {Event} e The event
22423      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22424      */
22425     notifyOut : function(dd, e, data){
22426         if(this.lastOverNode){
22427             this.onNodeOut(this.lastOverNode, dd, e, data);
22428             this.lastOverNode = null;
22429         }
22430     },
22431
22432     /**
22433      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22434      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22435      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22436      * otherwise it will call {@link #onContainerDrop}.
22437      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22438      * @param {Event} e The event
22439      * @param {Object} data An object containing arbitrary data supplied by the drag source
22440      * @return {Boolean} True if the drop was valid, else false
22441      */
22442     notifyDrop : function(dd, e, data){
22443         if(this.lastOverNode){
22444             this.onNodeOut(this.lastOverNode, dd, e, data);
22445             this.lastOverNode = null;
22446         }
22447         var n = this.getTargetFromEvent(e);
22448         return n ?
22449             this.onNodeDrop(n, dd, e, data) :
22450             this.onContainerDrop(dd, e, data);
22451     },
22452
22453     // private
22454     triggerCacheRefresh : function(){
22455         Roo.dd.DDM.refreshCache(this.groups);
22456     }  
22457 });/*
22458  * Based on:
22459  * Ext JS Library 1.1.1
22460  * Copyright(c) 2006-2007, Ext JS, LLC.
22461  *
22462  * Originally Released Under LGPL - original licence link has changed is not relivant.
22463  *
22464  * Fork - LGPL
22465  * <script type="text/javascript">
22466  */
22467
22468
22469 /**
22470  * @class Roo.data.SortTypes
22471  * @singleton
22472  * Defines the default sorting (casting?) comparison functions used when sorting data.
22473  */
22474 Roo.data.SortTypes = {
22475     /**
22476      * Default sort that does nothing
22477      * @param {Mixed} s The value being converted
22478      * @return {Mixed} The comparison value
22479      */
22480     none : function(s){
22481         return s;
22482     },
22483     
22484     /**
22485      * The regular expression used to strip tags
22486      * @type {RegExp}
22487      * @property
22488      */
22489     stripTagsRE : /<\/?[^>]+>/gi,
22490     
22491     /**
22492      * Strips all HTML tags to sort on text only
22493      * @param {Mixed} s The value being converted
22494      * @return {String} The comparison value
22495      */
22496     asText : function(s){
22497         return String(s).replace(this.stripTagsRE, "");
22498     },
22499     
22500     /**
22501      * Strips all HTML tags to sort on text only - Case insensitive
22502      * @param {Mixed} s The value being converted
22503      * @return {String} The comparison value
22504      */
22505     asUCText : function(s){
22506         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22507     },
22508     
22509     /**
22510      * Case insensitive string
22511      * @param {Mixed} s The value being converted
22512      * @return {String} The comparison value
22513      */
22514     asUCString : function(s) {
22515         return String(s).toUpperCase();
22516     },
22517     
22518     /**
22519      * Date sorting
22520      * @param {Mixed} s The value being converted
22521      * @return {Number} The comparison value
22522      */
22523     asDate : function(s) {
22524         if(!s){
22525             return 0;
22526         }
22527         if(s instanceof Date){
22528             return s.getTime();
22529         }
22530         return Date.parse(String(s));
22531     },
22532     
22533     /**
22534      * Float sorting
22535      * @param {Mixed} s The value being converted
22536      * @return {Float} The comparison value
22537      */
22538     asFloat : function(s) {
22539         var val = parseFloat(String(s).replace(/,/g, ""));
22540         if(isNaN(val)) {
22541             val = 0;
22542         }
22543         return val;
22544     },
22545     
22546     /**
22547      * Integer sorting
22548      * @param {Mixed} s The value being converted
22549      * @return {Number} The comparison value
22550      */
22551     asInt : function(s) {
22552         var val = parseInt(String(s).replace(/,/g, ""));
22553         if(isNaN(val)) {
22554             val = 0;
22555         }
22556         return val;
22557     }
22558 };/*
22559  * Based on:
22560  * Ext JS Library 1.1.1
22561  * Copyright(c) 2006-2007, Ext JS, LLC.
22562  *
22563  * Originally Released Under LGPL - original licence link has changed is not relivant.
22564  *
22565  * Fork - LGPL
22566  * <script type="text/javascript">
22567  */
22568
22569 /**
22570 * @class Roo.data.Record
22571  * Instances of this class encapsulate both record <em>definition</em> information, and record
22572  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22573  * to access Records cached in an {@link Roo.data.Store} object.<br>
22574  * <p>
22575  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22576  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22577  * objects.<br>
22578  * <p>
22579  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22580  * @constructor
22581  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22582  * {@link #create}. The parameters are the same.
22583  * @param {Array} data An associative Array of data values keyed by the field name.
22584  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22585  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22586  * not specified an integer id is generated.
22587  */
22588 Roo.data.Record = function(data, id){
22589     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22590     this.data = data;
22591 };
22592
22593 /**
22594  * Generate a constructor for a specific record layout.
22595  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22596  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22597  * Each field definition object may contain the following properties: <ul>
22598  * <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,
22599  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22600  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22601  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22602  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22603  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22604  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22605  * this may be omitted.</p></li>
22606  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22607  * <ul><li>auto (Default, implies no conversion)</li>
22608  * <li>string</li>
22609  * <li>int</li>
22610  * <li>float</li>
22611  * <li>boolean</li>
22612  * <li>date</li></ul></p></li>
22613  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22614  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22615  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22616  * by the Reader into an object that will be stored in the Record. It is passed the
22617  * following parameters:<ul>
22618  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22619  * </ul></p></li>
22620  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22621  * </ul>
22622  * <br>usage:<br><pre><code>
22623 var TopicRecord = Roo.data.Record.create(
22624     {name: 'title', mapping: 'topic_title'},
22625     {name: 'author', mapping: 'username'},
22626     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22627     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22628     {name: 'lastPoster', mapping: 'user2'},
22629     {name: 'excerpt', mapping: 'post_text'}
22630 );
22631
22632 var myNewRecord = new TopicRecord({
22633     title: 'Do my job please',
22634     author: 'noobie',
22635     totalPosts: 1,
22636     lastPost: new Date(),
22637     lastPoster: 'Animal',
22638     excerpt: 'No way dude!'
22639 });
22640 myStore.add(myNewRecord);
22641 </code></pre>
22642  * @method create
22643  * @static
22644  */
22645 Roo.data.Record.create = function(o){
22646     var f = function(){
22647         f.superclass.constructor.apply(this, arguments);
22648     };
22649     Roo.extend(f, Roo.data.Record);
22650     var p = f.prototype;
22651     p.fields = new Roo.util.MixedCollection(false, function(field){
22652         return field.name;
22653     });
22654     for(var i = 0, len = o.length; i < len; i++){
22655         p.fields.add(new Roo.data.Field(o[i]));
22656     }
22657     f.getField = function(name){
22658         return p.fields.get(name);  
22659     };
22660     return f;
22661 };
22662
22663 Roo.data.Record.AUTO_ID = 1000;
22664 Roo.data.Record.EDIT = 'edit';
22665 Roo.data.Record.REJECT = 'reject';
22666 Roo.data.Record.COMMIT = 'commit';
22667
22668 Roo.data.Record.prototype = {
22669     /**
22670      * Readonly flag - true if this record has been modified.
22671      * @type Boolean
22672      */
22673     dirty : false,
22674     editing : false,
22675     error: null,
22676     modified: null,
22677
22678     // private
22679     join : function(store){
22680         this.store = store;
22681     },
22682
22683     /**
22684      * Set the named field to the specified value.
22685      * @param {String} name The name of the field to set.
22686      * @param {Object} value The value to set the field to.
22687      */
22688     set : function(name, value){
22689         if(this.data[name] == value){
22690             return;
22691         }
22692         this.dirty = true;
22693         if(!this.modified){
22694             this.modified = {};
22695         }
22696         if(typeof this.modified[name] == 'undefined'){
22697             this.modified[name] = this.data[name];
22698         }
22699         this.data[name] = value;
22700         if(!this.editing && this.store){
22701             this.store.afterEdit(this);
22702         }       
22703     },
22704
22705     /**
22706      * Get the value of the named field.
22707      * @param {String} name The name of the field to get the value of.
22708      * @return {Object} The value of the field.
22709      */
22710     get : function(name){
22711         return this.data[name]; 
22712     },
22713
22714     // private
22715     beginEdit : function(){
22716         this.editing = true;
22717         this.modified = {}; 
22718     },
22719
22720     // private
22721     cancelEdit : function(){
22722         this.editing = false;
22723         delete this.modified;
22724     },
22725
22726     // private
22727     endEdit : function(){
22728         this.editing = false;
22729         if(this.dirty && this.store){
22730             this.store.afterEdit(this);
22731         }
22732     },
22733
22734     /**
22735      * Usually called by the {@link Roo.data.Store} which owns the Record.
22736      * Rejects all changes made to the Record since either creation, or the last commit operation.
22737      * Modified fields are reverted to their original values.
22738      * <p>
22739      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22740      * of reject operations.
22741      */
22742     reject : function(){
22743         var m = this.modified;
22744         for(var n in m){
22745             if(typeof m[n] != "function"){
22746                 this.data[n] = m[n];
22747             }
22748         }
22749         this.dirty = false;
22750         delete this.modified;
22751         this.editing = false;
22752         if(this.store){
22753             this.store.afterReject(this);
22754         }
22755     },
22756
22757     /**
22758      * Usually called by the {@link Roo.data.Store} which owns the Record.
22759      * Commits all changes made to the Record since either creation, or the last commit operation.
22760      * <p>
22761      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22762      * of commit operations.
22763      */
22764     commit : function(){
22765         this.dirty = false;
22766         delete this.modified;
22767         this.editing = false;
22768         if(this.store){
22769             this.store.afterCommit(this);
22770         }
22771     },
22772
22773     // private
22774     hasError : function(){
22775         return this.error != null;
22776     },
22777
22778     // private
22779     clearError : function(){
22780         this.error = null;
22781     },
22782
22783     /**
22784      * Creates a copy of this record.
22785      * @param {String} id (optional) A new record id if you don't want to use this record's id
22786      * @return {Record}
22787      */
22788     copy : function(newId) {
22789         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22790     }
22791 };/*
22792  * Based on:
22793  * Ext JS Library 1.1.1
22794  * Copyright(c) 2006-2007, Ext JS, LLC.
22795  *
22796  * Originally Released Under LGPL - original licence link has changed is not relivant.
22797  *
22798  * Fork - LGPL
22799  * <script type="text/javascript">
22800  */
22801
22802
22803
22804 /**
22805  * @class Roo.data.Store
22806  * @extends Roo.util.Observable
22807  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22808  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22809  * <p>
22810  * 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
22811  * has no knowledge of the format of the data returned by the Proxy.<br>
22812  * <p>
22813  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22814  * instances from the data object. These records are cached and made available through accessor functions.
22815  * @constructor
22816  * Creates a new Store.
22817  * @param {Object} config A config object containing the objects needed for the Store to access data,
22818  * and read the data into Records.
22819  */
22820 Roo.data.Store = function(config){
22821     this.data = new Roo.util.MixedCollection(false);
22822     this.data.getKey = function(o){
22823         return o.id;
22824     };
22825     this.baseParams = {};
22826     // private
22827     this.paramNames = {
22828         "start" : "start",
22829         "limit" : "limit",
22830         "sort" : "sort",
22831         "dir" : "dir",
22832         "multisort" : "_multisort"
22833     };
22834
22835     if(config && config.data){
22836         this.inlineData = config.data;
22837         delete config.data;
22838     }
22839
22840     Roo.apply(this, config);
22841     
22842     if(this.reader){ // reader passed
22843         this.reader = Roo.factory(this.reader, Roo.data);
22844         this.reader.xmodule = this.xmodule || false;
22845         if(!this.recordType){
22846             this.recordType = this.reader.recordType;
22847         }
22848         if(this.reader.onMetaChange){
22849             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22850         }
22851     }
22852
22853     if(this.recordType){
22854         this.fields = this.recordType.prototype.fields;
22855     }
22856     this.modified = [];
22857
22858     this.addEvents({
22859         /**
22860          * @event datachanged
22861          * Fires when the data cache has changed, and a widget which is using this Store
22862          * as a Record cache should refresh its view.
22863          * @param {Store} this
22864          */
22865         datachanged : true,
22866         /**
22867          * @event metachange
22868          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22869          * @param {Store} this
22870          * @param {Object} meta The JSON metadata
22871          */
22872         metachange : true,
22873         /**
22874          * @event add
22875          * Fires when Records have been added to the Store
22876          * @param {Store} this
22877          * @param {Roo.data.Record[]} records The array of Records added
22878          * @param {Number} index The index at which the record(s) were added
22879          */
22880         add : true,
22881         /**
22882          * @event remove
22883          * Fires when a Record has been removed from the Store
22884          * @param {Store} this
22885          * @param {Roo.data.Record} record The Record that was removed
22886          * @param {Number} index The index at which the record was removed
22887          */
22888         remove : true,
22889         /**
22890          * @event update
22891          * Fires when a Record has been updated
22892          * @param {Store} this
22893          * @param {Roo.data.Record} record The Record that was updated
22894          * @param {String} operation The update operation being performed.  Value may be one of:
22895          * <pre><code>
22896  Roo.data.Record.EDIT
22897  Roo.data.Record.REJECT
22898  Roo.data.Record.COMMIT
22899          * </code></pre>
22900          */
22901         update : true,
22902         /**
22903          * @event clear
22904          * Fires when the data cache has been cleared.
22905          * @param {Store} this
22906          */
22907         clear : true,
22908         /**
22909          * @event beforeload
22910          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22911          * the load action will be canceled.
22912          * @param {Store} this
22913          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22914          */
22915         beforeload : true,
22916         /**
22917          * @event beforeloadadd
22918          * Fires after a new set of Records has been loaded.
22919          * @param {Store} this
22920          * @param {Roo.data.Record[]} records The Records that were loaded
22921          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22922          */
22923         beforeloadadd : true,
22924         /**
22925          * @event load
22926          * Fires after a new set of Records has been loaded, before they are added to the store.
22927          * @param {Store} this
22928          * @param {Roo.data.Record[]} records The Records that were loaded
22929          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22930          * @params {Object} return from reader
22931          */
22932         load : true,
22933         /**
22934          * @event loadexception
22935          * Fires if an exception occurs in the Proxy during loading.
22936          * Called with the signature of the Proxy's "loadexception" event.
22937          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22938          * 
22939          * @param {Proxy} 
22940          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22941          * @param {Object} load options 
22942          * @param {Object} jsonData from your request (normally this contains the Exception)
22943          */
22944         loadexception : true
22945     });
22946     
22947     if(this.proxy){
22948         this.proxy = Roo.factory(this.proxy, Roo.data);
22949         this.proxy.xmodule = this.xmodule || false;
22950         this.relayEvents(this.proxy,  ["loadexception"]);
22951     }
22952     this.sortToggle = {};
22953     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22954
22955     Roo.data.Store.superclass.constructor.call(this);
22956
22957     if(this.inlineData){
22958         this.loadData(this.inlineData);
22959         delete this.inlineData;
22960     }
22961 };
22962
22963 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22964      /**
22965     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22966     * without a remote query - used by combo/forms at present.
22967     */
22968     
22969     /**
22970     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22971     */
22972     /**
22973     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22974     */
22975     /**
22976     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22977     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22978     */
22979     /**
22980     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22981     * on any HTTP request
22982     */
22983     /**
22984     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22985     */
22986     /**
22987     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22988     */
22989     multiSort: false,
22990     /**
22991     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22992     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22993     */
22994     remoteSort : false,
22995
22996     /**
22997     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22998      * loaded or when a record is removed. (defaults to false).
22999     */
23000     pruneModifiedRecords : false,
23001
23002     // private
23003     lastOptions : null,
23004
23005     /**
23006      * Add Records to the Store and fires the add event.
23007      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23008      */
23009     add : function(records){
23010         records = [].concat(records);
23011         for(var i = 0, len = records.length; i < len; i++){
23012             records[i].join(this);
23013         }
23014         var index = this.data.length;
23015         this.data.addAll(records);
23016         this.fireEvent("add", this, records, index);
23017     },
23018
23019     /**
23020      * Remove a Record from the Store and fires the remove event.
23021      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23022      */
23023     remove : function(record){
23024         var index = this.data.indexOf(record);
23025         this.data.removeAt(index);
23026  
23027         if(this.pruneModifiedRecords){
23028             this.modified.remove(record);
23029         }
23030         this.fireEvent("remove", this, record, index);
23031     },
23032
23033     /**
23034      * Remove all Records from the Store and fires the clear event.
23035      */
23036     removeAll : function(){
23037         this.data.clear();
23038         if(this.pruneModifiedRecords){
23039             this.modified = [];
23040         }
23041         this.fireEvent("clear", this);
23042     },
23043
23044     /**
23045      * Inserts Records to the Store at the given index and fires the add event.
23046      * @param {Number} index The start index at which to insert the passed Records.
23047      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23048      */
23049     insert : function(index, records){
23050         records = [].concat(records);
23051         for(var i = 0, len = records.length; i < len; i++){
23052             this.data.insert(index, records[i]);
23053             records[i].join(this);
23054         }
23055         this.fireEvent("add", this, records, index);
23056     },
23057
23058     /**
23059      * Get the index within the cache of the passed Record.
23060      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23061      * @return {Number} The index of the passed Record. Returns -1 if not found.
23062      */
23063     indexOf : function(record){
23064         return this.data.indexOf(record);
23065     },
23066
23067     /**
23068      * Get the index within the cache of the Record with the passed id.
23069      * @param {String} id The id of the Record to find.
23070      * @return {Number} The index of the Record. Returns -1 if not found.
23071      */
23072     indexOfId : function(id){
23073         return this.data.indexOfKey(id);
23074     },
23075
23076     /**
23077      * Get the Record with the specified id.
23078      * @param {String} id The id of the Record to find.
23079      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23080      */
23081     getById : function(id){
23082         return this.data.key(id);
23083     },
23084
23085     /**
23086      * Get the Record at the specified index.
23087      * @param {Number} index The index of the Record to find.
23088      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23089      */
23090     getAt : function(index){
23091         return this.data.itemAt(index);
23092     },
23093
23094     /**
23095      * Returns a range of Records between specified indices.
23096      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23097      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23098      * @return {Roo.data.Record[]} An array of Records
23099      */
23100     getRange : function(start, end){
23101         return this.data.getRange(start, end);
23102     },
23103
23104     // private
23105     storeOptions : function(o){
23106         o = Roo.apply({}, o);
23107         delete o.callback;
23108         delete o.scope;
23109         this.lastOptions = o;
23110     },
23111
23112     /**
23113      * Loads the Record cache from the configured Proxy using the configured Reader.
23114      * <p>
23115      * If using remote paging, then the first load call must specify the <em>start</em>
23116      * and <em>limit</em> properties in the options.params property to establish the initial
23117      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23118      * <p>
23119      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23120      * and this call will return before the new data has been loaded. Perform any post-processing
23121      * in a callback function, or in a "load" event handler.</strong>
23122      * <p>
23123      * @param {Object} options An object containing properties which control loading options:<ul>
23124      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23125      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23126      * passed the following arguments:<ul>
23127      * <li>r : Roo.data.Record[]</li>
23128      * <li>options: Options object from the load call</li>
23129      * <li>success: Boolean success indicator</li></ul></li>
23130      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23131      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23132      * </ul>
23133      */
23134     load : function(options){
23135         options = options || {};
23136         if(this.fireEvent("beforeload", this, options) !== false){
23137             this.storeOptions(options);
23138             var p = Roo.apply(options.params || {}, this.baseParams);
23139             // if meta was not loaded from remote source.. try requesting it.
23140             if (!this.reader.metaFromRemote) {
23141                 p._requestMeta = 1;
23142             }
23143             if(this.sortInfo && this.remoteSort){
23144                 var pn = this.paramNames;
23145                 p[pn["sort"]] = this.sortInfo.field;
23146                 p[pn["dir"]] = this.sortInfo.direction;
23147             }
23148             if (this.multiSort) {
23149                 var pn = this.paramNames;
23150                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23151             }
23152             
23153             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23154         }
23155     },
23156
23157     /**
23158      * Reloads the Record cache from the configured Proxy using the configured Reader and
23159      * the options from the last load operation performed.
23160      * @param {Object} options (optional) An object containing properties which may override the options
23161      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23162      * the most recently used options are reused).
23163      */
23164     reload : function(options){
23165         this.load(Roo.applyIf(options||{}, this.lastOptions));
23166     },
23167
23168     // private
23169     // Called as a callback by the Reader during a load operation.
23170     loadRecords : function(o, options, success){
23171         if(!o || success === false){
23172             if(success !== false){
23173                 this.fireEvent("load", this, [], options, o);
23174             }
23175             if(options.callback){
23176                 options.callback.call(options.scope || this, [], options, false);
23177             }
23178             return;
23179         }
23180         // if data returned failure - throw an exception.
23181         if (o.success === false) {
23182             // show a message if no listener is registered.
23183             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23184                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23185             }
23186             // loadmask wil be hooked into this..
23187             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23188             return;
23189         }
23190         var r = o.records, t = o.totalRecords || r.length;
23191         
23192         this.fireEvent("beforeloadadd", this, r, options, o);
23193         
23194         if(!options || options.add !== true){
23195             if(this.pruneModifiedRecords){
23196                 this.modified = [];
23197             }
23198             for(var i = 0, len = r.length; i < len; i++){
23199                 r[i].join(this);
23200             }
23201             if(this.snapshot){
23202                 this.data = this.snapshot;
23203                 delete this.snapshot;
23204             }
23205             this.data.clear();
23206             this.data.addAll(r);
23207             this.totalLength = t;
23208             this.applySort();
23209             this.fireEvent("datachanged", this);
23210         }else{
23211             this.totalLength = Math.max(t, this.data.length+r.length);
23212             this.add(r);
23213         }
23214         
23215         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23216                 
23217             var e = new Roo.data.Record({});
23218
23219             e.set(this.parent.displayField, this.parent.emptyTitle);
23220             e.set(this.parent.valueField, '');
23221
23222             this.insert(0, e);
23223         }
23224             
23225         this.fireEvent("load", this, r, options, o);
23226         if(options.callback){
23227             options.callback.call(options.scope || this, r, options, true);
23228         }
23229     },
23230
23231
23232     /**
23233      * Loads data from a passed data block. A Reader which understands the format of the data
23234      * must have been configured in the constructor.
23235      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23236      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23237      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23238      */
23239     loadData : function(o, append){
23240         var r = this.reader.readRecords(o);
23241         this.loadRecords(r, {add: append}, true);
23242     },
23243
23244     /**
23245      * Gets the number of cached records.
23246      * <p>
23247      * <em>If using paging, this may not be the total size of the dataset. If the data object
23248      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23249      * the data set size</em>
23250      */
23251     getCount : function(){
23252         return this.data.length || 0;
23253     },
23254
23255     /**
23256      * Gets the total number of records in the dataset as returned by the server.
23257      * <p>
23258      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23259      * the dataset size</em>
23260      */
23261     getTotalCount : function(){
23262         return this.totalLength || 0;
23263     },
23264
23265     /**
23266      * Returns the sort state of the Store as an object with two properties:
23267      * <pre><code>
23268  field {String} The name of the field by which the Records are sorted
23269  direction {String} The sort order, "ASC" or "DESC"
23270      * </code></pre>
23271      */
23272     getSortState : function(){
23273         return this.sortInfo;
23274     },
23275
23276     // private
23277     applySort : function(){
23278         if(this.sortInfo && !this.remoteSort){
23279             var s = this.sortInfo, f = s.field;
23280             var st = this.fields.get(f).sortType;
23281             var fn = function(r1, r2){
23282                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23283                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23284             };
23285             this.data.sort(s.direction, fn);
23286             if(this.snapshot && this.snapshot != this.data){
23287                 this.snapshot.sort(s.direction, fn);
23288             }
23289         }
23290     },
23291
23292     /**
23293      * Sets the default sort column and order to be used by the next load operation.
23294      * @param {String} fieldName The name of the field to sort by.
23295      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23296      */
23297     setDefaultSort : function(field, dir){
23298         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23299     },
23300
23301     /**
23302      * Sort the Records.
23303      * If remote sorting is used, the sort is performed on the server, and the cache is
23304      * reloaded. If local sorting is used, the cache is sorted internally.
23305      * @param {String} fieldName The name of the field to sort by.
23306      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23307      */
23308     sort : function(fieldName, dir){
23309         var f = this.fields.get(fieldName);
23310         if(!dir){
23311             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23312             
23313             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23314                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23315             }else{
23316                 dir = f.sortDir;
23317             }
23318         }
23319         this.sortToggle[f.name] = dir;
23320         this.sortInfo = {field: f.name, direction: dir};
23321         if(!this.remoteSort){
23322             this.applySort();
23323             this.fireEvent("datachanged", this);
23324         }else{
23325             this.load(this.lastOptions);
23326         }
23327     },
23328
23329     /**
23330      * Calls the specified function for each of the Records in the cache.
23331      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23332      * Returning <em>false</em> aborts and exits the iteration.
23333      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23334      */
23335     each : function(fn, scope){
23336         this.data.each(fn, scope);
23337     },
23338
23339     /**
23340      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23341      * (e.g., during paging).
23342      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23343      */
23344     getModifiedRecords : function(){
23345         return this.modified;
23346     },
23347
23348     // private
23349     createFilterFn : function(property, value, anyMatch){
23350         if(!value.exec){ // not a regex
23351             value = String(value);
23352             if(value.length == 0){
23353                 return false;
23354             }
23355             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23356         }
23357         return function(r){
23358             return value.test(r.data[property]);
23359         };
23360     },
23361
23362     /**
23363      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23364      * @param {String} property A field on your records
23365      * @param {Number} start The record index to start at (defaults to 0)
23366      * @param {Number} end The last record index to include (defaults to length - 1)
23367      * @return {Number} The sum
23368      */
23369     sum : function(property, start, end){
23370         var rs = this.data.items, v = 0;
23371         start = start || 0;
23372         end = (end || end === 0) ? end : rs.length-1;
23373
23374         for(var i = start; i <= end; i++){
23375             v += (rs[i].data[property] || 0);
23376         }
23377         return v;
23378     },
23379
23380     /**
23381      * Filter the records by a specified property.
23382      * @param {String} field A field on your records
23383      * @param {String/RegExp} value Either a string that the field
23384      * should start with or a RegExp to test against the field
23385      * @param {Boolean} anyMatch True to match any part not just the beginning
23386      */
23387     filter : function(property, value, anyMatch){
23388         var fn = this.createFilterFn(property, value, anyMatch);
23389         return fn ? this.filterBy(fn) : this.clearFilter();
23390     },
23391
23392     /**
23393      * Filter by a function. The specified function will be called with each
23394      * record in this data source. If the function returns true the record is included,
23395      * otherwise it is filtered.
23396      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23397      * @param {Object} scope (optional) The scope of the function (defaults to this)
23398      */
23399     filterBy : function(fn, scope){
23400         this.snapshot = this.snapshot || this.data;
23401         this.data = this.queryBy(fn, scope||this);
23402         this.fireEvent("datachanged", this);
23403     },
23404
23405     /**
23406      * Query the records by a specified property.
23407      * @param {String} field A field on your records
23408      * @param {String/RegExp} value Either a string that the field
23409      * should start with or a RegExp to test against the field
23410      * @param {Boolean} anyMatch True to match any part not just the beginning
23411      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23412      */
23413     query : function(property, value, anyMatch){
23414         var fn = this.createFilterFn(property, value, anyMatch);
23415         return fn ? this.queryBy(fn) : this.data.clone();
23416     },
23417
23418     /**
23419      * Query by a function. The specified function will be called with each
23420      * record in this data source. If the function returns true the record is included
23421      * in the results.
23422      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23423      * @param {Object} scope (optional) The scope of the function (defaults to this)
23424       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23425      **/
23426     queryBy : function(fn, scope){
23427         var data = this.snapshot || this.data;
23428         return data.filterBy(fn, scope||this);
23429     },
23430
23431     /**
23432      * Collects unique values for a particular dataIndex from this store.
23433      * @param {String} dataIndex The property to collect
23434      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23435      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23436      * @return {Array} An array of the unique values
23437      **/
23438     collect : function(dataIndex, allowNull, bypassFilter){
23439         var d = (bypassFilter === true && this.snapshot) ?
23440                 this.snapshot.items : this.data.items;
23441         var v, sv, r = [], l = {};
23442         for(var i = 0, len = d.length; i < len; i++){
23443             v = d[i].data[dataIndex];
23444             sv = String(v);
23445             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23446                 l[sv] = true;
23447                 r[r.length] = v;
23448             }
23449         }
23450         return r;
23451     },
23452
23453     /**
23454      * Revert to a view of the Record cache with no filtering applied.
23455      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23456      */
23457     clearFilter : function(suppressEvent){
23458         if(this.snapshot && this.snapshot != this.data){
23459             this.data = this.snapshot;
23460             delete this.snapshot;
23461             if(suppressEvent !== true){
23462                 this.fireEvent("datachanged", this);
23463             }
23464         }
23465     },
23466
23467     // private
23468     afterEdit : function(record){
23469         if(this.modified.indexOf(record) == -1){
23470             this.modified.push(record);
23471         }
23472         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23473     },
23474     
23475     // private
23476     afterReject : function(record){
23477         this.modified.remove(record);
23478         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23479     },
23480
23481     // private
23482     afterCommit : function(record){
23483         this.modified.remove(record);
23484         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23485     },
23486
23487     /**
23488      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23489      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23490      */
23491     commitChanges : function(){
23492         var m = this.modified.slice(0);
23493         this.modified = [];
23494         for(var i = 0, len = m.length; i < len; i++){
23495             m[i].commit();
23496         }
23497     },
23498
23499     /**
23500      * Cancel outstanding changes on all changed records.
23501      */
23502     rejectChanges : function(){
23503         var m = this.modified.slice(0);
23504         this.modified = [];
23505         for(var i = 0, len = m.length; i < len; i++){
23506             m[i].reject();
23507         }
23508     },
23509
23510     onMetaChange : function(meta, rtype, o){
23511         this.recordType = rtype;
23512         this.fields = rtype.prototype.fields;
23513         delete this.snapshot;
23514         this.sortInfo = meta.sortInfo || this.sortInfo;
23515         this.modified = [];
23516         this.fireEvent('metachange', this, this.reader.meta);
23517     },
23518     
23519     moveIndex : function(data, type)
23520     {
23521         var index = this.indexOf(data);
23522         
23523         var newIndex = index + type;
23524         
23525         this.remove(data);
23526         
23527         this.insert(newIndex, data);
23528         
23529     }
23530 });/*
23531  * Based on:
23532  * Ext JS Library 1.1.1
23533  * Copyright(c) 2006-2007, Ext JS, LLC.
23534  *
23535  * Originally Released Under LGPL - original licence link has changed is not relivant.
23536  *
23537  * Fork - LGPL
23538  * <script type="text/javascript">
23539  */
23540
23541 /**
23542  * @class Roo.data.SimpleStore
23543  * @extends Roo.data.Store
23544  * Small helper class to make creating Stores from Array data easier.
23545  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23546  * @cfg {Array} fields An array of field definition objects, or field name strings.
23547  * @cfg {Object} an existing reader (eg. copied from another store)
23548  * @cfg {Array} data The multi-dimensional array of data
23549  * @constructor
23550  * @param {Object} config
23551  */
23552 Roo.data.SimpleStore = function(config)
23553 {
23554     Roo.data.SimpleStore.superclass.constructor.call(this, {
23555         isLocal : true,
23556         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23557                 id: config.id
23558             },
23559             Roo.data.Record.create(config.fields)
23560         ),
23561         proxy : new Roo.data.MemoryProxy(config.data)
23562     });
23563     this.load();
23564 };
23565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23566  * Based on:
23567  * Ext JS Library 1.1.1
23568  * Copyright(c) 2006-2007, Ext JS, LLC.
23569  *
23570  * Originally Released Under LGPL - original licence link has changed is not relivant.
23571  *
23572  * Fork - LGPL
23573  * <script type="text/javascript">
23574  */
23575
23576 /**
23577 /**
23578  * @extends Roo.data.Store
23579  * @class Roo.data.JsonStore
23580  * Small helper class to make creating Stores for JSON data easier. <br/>
23581 <pre><code>
23582 var store = new Roo.data.JsonStore({
23583     url: 'get-images.php',
23584     root: 'images',
23585     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23586 });
23587 </code></pre>
23588  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23589  * JsonReader and HttpProxy (unless inline data is provided).</b>
23590  * @cfg {Array} fields An array of field definition objects, or field name strings.
23591  * @constructor
23592  * @param {Object} config
23593  */
23594 Roo.data.JsonStore = function(c){
23595     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23596         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23597         reader: new Roo.data.JsonReader(c, c.fields)
23598     }));
23599 };
23600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23601  * Based on:
23602  * Ext JS Library 1.1.1
23603  * Copyright(c) 2006-2007, Ext JS, LLC.
23604  *
23605  * Originally Released Under LGPL - original licence link has changed is not relivant.
23606  *
23607  * Fork - LGPL
23608  * <script type="text/javascript">
23609  */
23610
23611  
23612 Roo.data.Field = function(config){
23613     if(typeof config == "string"){
23614         config = {name: config};
23615     }
23616     Roo.apply(this, config);
23617     
23618     if(!this.type){
23619         this.type = "auto";
23620     }
23621     
23622     var st = Roo.data.SortTypes;
23623     // named sortTypes are supported, here we look them up
23624     if(typeof this.sortType == "string"){
23625         this.sortType = st[this.sortType];
23626     }
23627     
23628     // set default sortType for strings and dates
23629     if(!this.sortType){
23630         switch(this.type){
23631             case "string":
23632                 this.sortType = st.asUCString;
23633                 break;
23634             case "date":
23635                 this.sortType = st.asDate;
23636                 break;
23637             default:
23638                 this.sortType = st.none;
23639         }
23640     }
23641
23642     // define once
23643     var stripRe = /[\$,%]/g;
23644
23645     // prebuilt conversion function for this field, instead of
23646     // switching every time we're reading a value
23647     if(!this.convert){
23648         var cv, dateFormat = this.dateFormat;
23649         switch(this.type){
23650             case "":
23651             case "auto":
23652             case undefined:
23653                 cv = function(v){ return v; };
23654                 break;
23655             case "string":
23656                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23657                 break;
23658             case "int":
23659                 cv = function(v){
23660                     return v !== undefined && v !== null && v !== '' ?
23661                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23662                     };
23663                 break;
23664             case "float":
23665                 cv = function(v){
23666                     return v !== undefined && v !== null && v !== '' ?
23667                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23668                     };
23669                 break;
23670             case "bool":
23671             case "boolean":
23672                 cv = function(v){ return v === true || v === "true" || v == 1; };
23673                 break;
23674             case "date":
23675                 cv = function(v){
23676                     if(!v){
23677                         return '';
23678                     }
23679                     if(v instanceof Date){
23680                         return v;
23681                     }
23682                     if(dateFormat){
23683                         if(dateFormat == "timestamp"){
23684                             return new Date(v*1000);
23685                         }
23686                         return Date.parseDate(v, dateFormat);
23687                     }
23688                     var parsed = Date.parse(v);
23689                     return parsed ? new Date(parsed) : null;
23690                 };
23691              break;
23692             
23693         }
23694         this.convert = cv;
23695     }
23696 };
23697
23698 Roo.data.Field.prototype = {
23699     dateFormat: null,
23700     defaultValue: "",
23701     mapping: null,
23702     sortType : null,
23703     sortDir : "ASC"
23704 };/*
23705  * Based on:
23706  * Ext JS Library 1.1.1
23707  * Copyright(c) 2006-2007, Ext JS, LLC.
23708  *
23709  * Originally Released Under LGPL - original licence link has changed is not relivant.
23710  *
23711  * Fork - LGPL
23712  * <script type="text/javascript">
23713  */
23714  
23715 // Base class for reading structured data from a data source.  This class is intended to be
23716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23717
23718 /**
23719  * @class Roo.data.DataReader
23720  * Base class for reading structured data from a data source.  This class is intended to be
23721  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23722  */
23723
23724 Roo.data.DataReader = function(meta, recordType){
23725     
23726     this.meta = meta;
23727     
23728     this.recordType = recordType instanceof Array ? 
23729         Roo.data.Record.create(recordType) : recordType;
23730 };
23731
23732 Roo.data.DataReader.prototype = {
23733      /**
23734      * Create an empty record
23735      * @param {Object} data (optional) - overlay some values
23736      * @return {Roo.data.Record} record created.
23737      */
23738     newRow :  function(d) {
23739         var da =  {};
23740         this.recordType.prototype.fields.each(function(c) {
23741             switch( c.type) {
23742                 case 'int' : da[c.name] = 0; break;
23743                 case 'date' : da[c.name] = new Date(); break;
23744                 case 'float' : da[c.name] = 0.0; break;
23745                 case 'boolean' : da[c.name] = false; break;
23746                 default : da[c.name] = ""; break;
23747             }
23748             
23749         });
23750         return new this.recordType(Roo.apply(da, d));
23751     }
23752     
23753 };/*
23754  * Based on:
23755  * Ext JS Library 1.1.1
23756  * Copyright(c) 2006-2007, Ext JS, LLC.
23757  *
23758  * Originally Released Under LGPL - original licence link has changed is not relivant.
23759  *
23760  * Fork - LGPL
23761  * <script type="text/javascript">
23762  */
23763
23764 /**
23765  * @class Roo.data.DataProxy
23766  * @extends Roo.data.Observable
23767  * This class is an abstract base class for implementations which provide retrieval of
23768  * unformatted data objects.<br>
23769  * <p>
23770  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23771  * (of the appropriate type which knows how to parse the data object) to provide a block of
23772  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23773  * <p>
23774  * Custom implementations must implement the load method as described in
23775  * {@link Roo.data.HttpProxy#load}.
23776  */
23777 Roo.data.DataProxy = function(){
23778     this.addEvents({
23779         /**
23780          * @event beforeload
23781          * Fires before a network request is made to retrieve a data object.
23782          * @param {Object} This DataProxy object.
23783          * @param {Object} params The params parameter to the load function.
23784          */
23785         beforeload : true,
23786         /**
23787          * @event load
23788          * Fires before the load method's callback is called.
23789          * @param {Object} This DataProxy object.
23790          * @param {Object} o The data object.
23791          * @param {Object} arg The callback argument object passed to the load function.
23792          */
23793         load : true,
23794         /**
23795          * @event loadexception
23796          * Fires if an Exception occurs during data retrieval.
23797          * @param {Object} This DataProxy object.
23798          * @param {Object} o The data object.
23799          * @param {Object} arg The callback argument object passed to the load function.
23800          * @param {Object} e The Exception.
23801          */
23802         loadexception : true
23803     });
23804     Roo.data.DataProxy.superclass.constructor.call(this);
23805 };
23806
23807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23808
23809     /**
23810      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23811      */
23812 /*
23813  * Based on:
23814  * Ext JS Library 1.1.1
23815  * Copyright(c) 2006-2007, Ext JS, LLC.
23816  *
23817  * Originally Released Under LGPL - original licence link has changed is not relivant.
23818  *
23819  * Fork - LGPL
23820  * <script type="text/javascript">
23821  */
23822 /**
23823  * @class Roo.data.MemoryProxy
23824  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23825  * to the Reader when its load method is called.
23826  * @constructor
23827  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23828  */
23829 Roo.data.MemoryProxy = function(data){
23830     if (data.data) {
23831         data = data.data;
23832     }
23833     Roo.data.MemoryProxy.superclass.constructor.call(this);
23834     this.data = data;
23835 };
23836
23837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23838     
23839     /**
23840      * Load data from the requested source (in this case an in-memory
23841      * data object passed to the constructor), read the data object into
23842      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23843      * process that block using the passed callback.
23844      * @param {Object} params This parameter is not used by the MemoryProxy class.
23845      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23846      * object into a block of Roo.data.Records.
23847      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23848      * The function must be passed <ul>
23849      * <li>The Record block object</li>
23850      * <li>The "arg" argument from the load function</li>
23851      * <li>A boolean success indicator</li>
23852      * </ul>
23853      * @param {Object} scope The scope in which to call the callback
23854      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23855      */
23856     load : function(params, reader, callback, scope, arg){
23857         params = params || {};
23858         var result;
23859         try {
23860             result = reader.readRecords(params.data ? params.data :this.data);
23861         }catch(e){
23862             this.fireEvent("loadexception", this, arg, null, e);
23863             callback.call(scope, null, arg, false);
23864             return;
23865         }
23866         callback.call(scope, result, arg, true);
23867     },
23868     
23869     // private
23870     update : function(params, records){
23871         
23872     }
23873 });/*
23874  * Based on:
23875  * Ext JS Library 1.1.1
23876  * Copyright(c) 2006-2007, Ext JS, LLC.
23877  *
23878  * Originally Released Under LGPL - original licence link has changed is not relivant.
23879  *
23880  * Fork - LGPL
23881  * <script type="text/javascript">
23882  */
23883 /**
23884  * @class Roo.data.HttpProxy
23885  * @extends Roo.data.DataProxy
23886  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23887  * configured to reference a certain URL.<br><br>
23888  * <p>
23889  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23890  * from which the running page was served.<br><br>
23891  * <p>
23892  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23893  * <p>
23894  * Be aware that to enable the browser to parse an XML document, the server must set
23895  * the Content-Type header in the HTTP response to "text/xml".
23896  * @constructor
23897  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23898  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23899  * will be used to make the request.
23900  */
23901 Roo.data.HttpProxy = function(conn){
23902     Roo.data.HttpProxy.superclass.constructor.call(this);
23903     // is conn a conn config or a real conn?
23904     this.conn = conn;
23905     this.useAjax = !conn || !conn.events;
23906   
23907 };
23908
23909 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23910     // thse are take from connection...
23911     
23912     /**
23913      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23914      */
23915     /**
23916      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23917      * extra parameters to each request made by this object. (defaults to undefined)
23918      */
23919     /**
23920      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23921      *  to each request made by this object. (defaults to undefined)
23922      */
23923     /**
23924      * @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)
23925      */
23926     /**
23927      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23928      */
23929      /**
23930      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23931      * @type Boolean
23932      */
23933   
23934
23935     /**
23936      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23937      * @type Boolean
23938      */
23939     /**
23940      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23941      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23942      * a finer-grained basis than the DataProxy events.
23943      */
23944     getConnection : function(){
23945         return this.useAjax ? Roo.Ajax : this.conn;
23946     },
23947
23948     /**
23949      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23950      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23951      * process that block using the passed callback.
23952      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23953      * for the request to the remote server.
23954      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23955      * object into a block of Roo.data.Records.
23956      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23957      * The function must be passed <ul>
23958      * <li>The Record block object</li>
23959      * <li>The "arg" argument from the load function</li>
23960      * <li>A boolean success indicator</li>
23961      * </ul>
23962      * @param {Object} scope The scope in which to call the callback
23963      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23964      */
23965     load : function(params, reader, callback, scope, arg){
23966         if(this.fireEvent("beforeload", this, params) !== false){
23967             var  o = {
23968                 params : params || {},
23969                 request: {
23970                     callback : callback,
23971                     scope : scope,
23972                     arg : arg
23973                 },
23974                 reader: reader,
23975                 callback : this.loadResponse,
23976                 scope: this
23977             };
23978             if(this.useAjax){
23979                 Roo.applyIf(o, this.conn);
23980                 if(this.activeRequest){
23981                     Roo.Ajax.abort(this.activeRequest);
23982                 }
23983                 this.activeRequest = Roo.Ajax.request(o);
23984             }else{
23985                 this.conn.request(o);
23986             }
23987         }else{
23988             callback.call(scope||this, null, arg, false);
23989         }
23990     },
23991
23992     // private
23993     loadResponse : function(o, success, response){
23994         delete this.activeRequest;
23995         if(!success){
23996             this.fireEvent("loadexception", this, o, response);
23997             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23998             return;
23999         }
24000         var result;
24001         try {
24002             result = o.reader.read(response);
24003         }catch(e){
24004             this.fireEvent("loadexception", this, o, response, e);
24005             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24006             return;
24007         }
24008         
24009         this.fireEvent("load", this, o, o.request.arg);
24010         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24011     },
24012
24013     // private
24014     update : function(dataSet){
24015
24016     },
24017
24018     // private
24019     updateResponse : function(dataSet){
24020
24021     }
24022 });/*
24023  * Based on:
24024  * Ext JS Library 1.1.1
24025  * Copyright(c) 2006-2007, Ext JS, LLC.
24026  *
24027  * Originally Released Under LGPL - original licence link has changed is not relivant.
24028  *
24029  * Fork - LGPL
24030  * <script type="text/javascript">
24031  */
24032
24033 /**
24034  * @class Roo.data.ScriptTagProxy
24035  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24036  * other than the originating domain of the running page.<br><br>
24037  * <p>
24038  * <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
24039  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24040  * <p>
24041  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24042  * source code that is used as the source inside a &lt;script> tag.<br><br>
24043  * <p>
24044  * In order for the browser to process the returned data, the server must wrap the data object
24045  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24046  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24047  * depending on whether the callback name was passed:
24048  * <p>
24049  * <pre><code>
24050 boolean scriptTag = false;
24051 String cb = request.getParameter("callback");
24052 if (cb != null) {
24053     scriptTag = true;
24054     response.setContentType("text/javascript");
24055 } else {
24056     response.setContentType("application/x-json");
24057 }
24058 Writer out = response.getWriter();
24059 if (scriptTag) {
24060     out.write(cb + "(");
24061 }
24062 out.print(dataBlock.toJsonString());
24063 if (scriptTag) {
24064     out.write(");");
24065 }
24066 </pre></code>
24067  *
24068  * @constructor
24069  * @param {Object} config A configuration object.
24070  */
24071 Roo.data.ScriptTagProxy = function(config){
24072     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24073     Roo.apply(this, config);
24074     this.head = document.getElementsByTagName("head")[0];
24075 };
24076
24077 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24078
24079 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24080     /**
24081      * @cfg {String} url The URL from which to request the data object.
24082      */
24083     /**
24084      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24085      */
24086     timeout : 30000,
24087     /**
24088      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24089      * the server the name of the callback function set up by the load call to process the returned data object.
24090      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24091      * javascript output which calls this named function passing the data object as its only parameter.
24092      */
24093     callbackParam : "callback",
24094     /**
24095      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24096      * name to the request.
24097      */
24098     nocache : true,
24099
24100     /**
24101      * Load data from the configured URL, read the data object into
24102      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24103      * process that block using the passed callback.
24104      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24105      * for the request to the remote server.
24106      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24107      * object into a block of Roo.data.Records.
24108      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24109      * The function must be passed <ul>
24110      * <li>The Record block object</li>
24111      * <li>The "arg" argument from the load function</li>
24112      * <li>A boolean success indicator</li>
24113      * </ul>
24114      * @param {Object} scope The scope in which to call the callback
24115      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24116      */
24117     load : function(params, reader, callback, scope, arg){
24118         if(this.fireEvent("beforeload", this, params) !== false){
24119
24120             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24121
24122             var url = this.url;
24123             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24124             if(this.nocache){
24125                 url += "&_dc=" + (new Date().getTime());
24126             }
24127             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24128             var trans = {
24129                 id : transId,
24130                 cb : "stcCallback"+transId,
24131                 scriptId : "stcScript"+transId,
24132                 params : params,
24133                 arg : arg,
24134                 url : url,
24135                 callback : callback,
24136                 scope : scope,
24137                 reader : reader
24138             };
24139             var conn = this;
24140
24141             window[trans.cb] = function(o){
24142                 conn.handleResponse(o, trans);
24143             };
24144
24145             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24146
24147             if(this.autoAbort !== false){
24148                 this.abort();
24149             }
24150
24151             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24152
24153             var script = document.createElement("script");
24154             script.setAttribute("src", url);
24155             script.setAttribute("type", "text/javascript");
24156             script.setAttribute("id", trans.scriptId);
24157             this.head.appendChild(script);
24158
24159             this.trans = trans;
24160         }else{
24161             callback.call(scope||this, null, arg, false);
24162         }
24163     },
24164
24165     // private
24166     isLoading : function(){
24167         return this.trans ? true : false;
24168     },
24169
24170     /**
24171      * Abort the current server request.
24172      */
24173     abort : function(){
24174         if(this.isLoading()){
24175             this.destroyTrans(this.trans);
24176         }
24177     },
24178
24179     // private
24180     destroyTrans : function(trans, isLoaded){
24181         this.head.removeChild(document.getElementById(trans.scriptId));
24182         clearTimeout(trans.timeoutId);
24183         if(isLoaded){
24184             window[trans.cb] = undefined;
24185             try{
24186                 delete window[trans.cb];
24187             }catch(e){}
24188         }else{
24189             // if hasn't been loaded, wait for load to remove it to prevent script error
24190             window[trans.cb] = function(){
24191                 window[trans.cb] = undefined;
24192                 try{
24193                     delete window[trans.cb];
24194                 }catch(e){}
24195             };
24196         }
24197     },
24198
24199     // private
24200     handleResponse : function(o, trans){
24201         this.trans = false;
24202         this.destroyTrans(trans, true);
24203         var result;
24204         try {
24205             result = trans.reader.readRecords(o);
24206         }catch(e){
24207             this.fireEvent("loadexception", this, o, trans.arg, e);
24208             trans.callback.call(trans.scope||window, null, trans.arg, false);
24209             return;
24210         }
24211         this.fireEvent("load", this, o, trans.arg);
24212         trans.callback.call(trans.scope||window, result, trans.arg, true);
24213     },
24214
24215     // private
24216     handleFailure : function(trans){
24217         this.trans = false;
24218         this.destroyTrans(trans, false);
24219         this.fireEvent("loadexception", this, null, trans.arg);
24220         trans.callback.call(trans.scope||window, null, trans.arg, false);
24221     }
24222 });/*
24223  * Based on:
24224  * Ext JS Library 1.1.1
24225  * Copyright(c) 2006-2007, Ext JS, LLC.
24226  *
24227  * Originally Released Under LGPL - original licence link has changed is not relivant.
24228  *
24229  * Fork - LGPL
24230  * <script type="text/javascript">
24231  */
24232
24233 /**
24234  * @class Roo.data.JsonReader
24235  * @extends Roo.data.DataReader
24236  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24237  * based on mappings in a provided Roo.data.Record constructor.
24238  * 
24239  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24240  * in the reply previously. 
24241  * 
24242  * <p>
24243  * Example code:
24244  * <pre><code>
24245 var RecordDef = Roo.data.Record.create([
24246     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24247     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24248 ]);
24249 var myReader = new Roo.data.JsonReader({
24250     totalProperty: "results",    // The property which contains the total dataset size (optional)
24251     root: "rows",                // The property which contains an Array of row objects
24252     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24253 }, RecordDef);
24254 </code></pre>
24255  * <p>
24256  * This would consume a JSON file like this:
24257  * <pre><code>
24258 { 'results': 2, 'rows': [
24259     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24260     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24261 }
24262 </code></pre>
24263  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24264  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24265  * paged from the remote server.
24266  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24267  * @cfg {String} root name of the property which contains the Array of row objects.
24268  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24269  * @cfg {Array} fields Array of field definition objects
24270  * @constructor
24271  * Create a new JsonReader
24272  * @param {Object} meta Metadata configuration options
24273  * @param {Object} recordType Either an Array of field definition objects,
24274  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24275  */
24276 Roo.data.JsonReader = function(meta, recordType){
24277     
24278     meta = meta || {};
24279     // set some defaults:
24280     Roo.applyIf(meta, {
24281         totalProperty: 'total',
24282         successProperty : 'success',
24283         root : 'data',
24284         id : 'id'
24285     });
24286     
24287     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24288 };
24289 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24290     
24291     /**
24292      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24293      * Used by Store query builder to append _requestMeta to params.
24294      * 
24295      */
24296     metaFromRemote : false,
24297     /**
24298      * This method is only used by a DataProxy which has retrieved data from a remote server.
24299      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24300      * @return {Object} data A data block which is used by an Roo.data.Store object as
24301      * a cache of Roo.data.Records.
24302      */
24303     read : function(response){
24304         var json = response.responseText;
24305        
24306         var o = /* eval:var:o */ eval("("+json+")");
24307         if(!o) {
24308             throw {message: "JsonReader.read: Json object not found"};
24309         }
24310         
24311         if(o.metaData){
24312             
24313             delete this.ef;
24314             this.metaFromRemote = true;
24315             this.meta = o.metaData;
24316             this.recordType = Roo.data.Record.create(o.metaData.fields);
24317             this.onMetaChange(this.meta, this.recordType, o);
24318         }
24319         return this.readRecords(o);
24320     },
24321
24322     // private function a store will implement
24323     onMetaChange : function(meta, recordType, o){
24324
24325     },
24326
24327     /**
24328          * @ignore
24329          */
24330     simpleAccess: function(obj, subsc) {
24331         return obj[subsc];
24332     },
24333
24334         /**
24335          * @ignore
24336          */
24337     getJsonAccessor: function(){
24338         var re = /[\[\.]/;
24339         return function(expr) {
24340             try {
24341                 return(re.test(expr))
24342                     ? new Function("obj", "return obj." + expr)
24343                     : function(obj){
24344                         return obj[expr];
24345                     };
24346             } catch(e){}
24347             return Roo.emptyFn;
24348         };
24349     }(),
24350
24351     /**
24352      * Create a data block containing Roo.data.Records from an XML document.
24353      * @param {Object} o An object which contains an Array of row objects in the property specified
24354      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24355      * which contains the total size of the dataset.
24356      * @return {Object} data A data block which is used by an Roo.data.Store object as
24357      * a cache of Roo.data.Records.
24358      */
24359     readRecords : function(o){
24360         /**
24361          * After any data loads, the raw JSON data is available for further custom processing.
24362          * @type Object
24363          */
24364         this.o = o;
24365         var s = this.meta, Record = this.recordType,
24366             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24367
24368 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24369         if (!this.ef) {
24370             if(s.totalProperty) {
24371                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24372                 }
24373                 if(s.successProperty) {
24374                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24375                 }
24376                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24377                 if (s.id) {
24378                         var g = this.getJsonAccessor(s.id);
24379                         this.getId = function(rec) {
24380                                 var r = g(rec);  
24381                                 return (r === undefined || r === "") ? null : r;
24382                         };
24383                 } else {
24384                         this.getId = function(){return null;};
24385                 }
24386             this.ef = [];
24387             for(var jj = 0; jj < fl; jj++){
24388                 f = fi[jj];
24389                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24390                 this.ef[jj] = this.getJsonAccessor(map);
24391             }
24392         }
24393
24394         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24395         if(s.totalProperty){
24396             var vt = parseInt(this.getTotal(o), 10);
24397             if(!isNaN(vt)){
24398                 totalRecords = vt;
24399             }
24400         }
24401         if(s.successProperty){
24402             var vs = this.getSuccess(o);
24403             if(vs === false || vs === 'false'){
24404                 success = false;
24405             }
24406         }
24407         var records = [];
24408         for(var i = 0; i < c; i++){
24409                 var n = root[i];
24410             var values = {};
24411             var id = this.getId(n);
24412             for(var j = 0; j < fl; j++){
24413                 f = fi[j];
24414             var v = this.ef[j](n);
24415             if (!f.convert) {
24416                 Roo.log('missing convert for ' + f.name);
24417                 Roo.log(f);
24418                 continue;
24419             }
24420             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24421             }
24422             var record = new Record(values, id);
24423             record.json = n;
24424             records[i] = record;
24425         }
24426         return {
24427             raw : o,
24428             success : success,
24429             records : records,
24430             totalRecords : totalRecords
24431         };
24432     }
24433 });/*
24434  * Based on:
24435  * Ext JS Library 1.1.1
24436  * Copyright(c) 2006-2007, Ext JS, LLC.
24437  *
24438  * Originally Released Under LGPL - original licence link has changed is not relivant.
24439  *
24440  * Fork - LGPL
24441  * <script type="text/javascript">
24442  */
24443
24444 /**
24445  * @class Roo.data.XmlReader
24446  * @extends Roo.data.DataReader
24447  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24448  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24449  * <p>
24450  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24451  * header in the HTTP response must be set to "text/xml".</em>
24452  * <p>
24453  * Example code:
24454  * <pre><code>
24455 var RecordDef = Roo.data.Record.create([
24456    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24457    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24458 ]);
24459 var myReader = new Roo.data.XmlReader({
24460    totalRecords: "results", // The element which contains the total dataset size (optional)
24461    record: "row",           // The repeated element which contains row information
24462    id: "id"                 // The element within the row that provides an ID for the record (optional)
24463 }, RecordDef);
24464 </code></pre>
24465  * <p>
24466  * This would consume an XML file like this:
24467  * <pre><code>
24468 &lt;?xml?>
24469 &lt;dataset>
24470  &lt;results>2&lt;/results>
24471  &lt;row>
24472    &lt;id>1&lt;/id>
24473    &lt;name>Bill&lt;/name>
24474    &lt;occupation>Gardener&lt;/occupation>
24475  &lt;/row>
24476  &lt;row>
24477    &lt;id>2&lt;/id>
24478    &lt;name>Ben&lt;/name>
24479    &lt;occupation>Horticulturalist&lt;/occupation>
24480  &lt;/row>
24481 &lt;/dataset>
24482 </code></pre>
24483  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24484  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24485  * paged from the remote server.
24486  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24487  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24488  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24489  * a record identifier value.
24490  * @constructor
24491  * Create a new XmlReader
24492  * @param {Object} meta Metadata configuration options
24493  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24494  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24495  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24496  */
24497 Roo.data.XmlReader = function(meta, recordType){
24498     meta = meta || {};
24499     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24500 };
24501 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24502     /**
24503      * This method is only used by a DataProxy which has retrieved data from a remote server.
24504          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24505          * to contain a method called 'responseXML' that returns an XML document object.
24506      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24507      * a cache of Roo.data.Records.
24508      */
24509     read : function(response){
24510         var doc = response.responseXML;
24511         if(!doc) {
24512             throw {message: "XmlReader.read: XML Document not available"};
24513         }
24514         return this.readRecords(doc);
24515     },
24516
24517     /**
24518      * Create a data block containing Roo.data.Records from an XML document.
24519          * @param {Object} doc A parsed XML document.
24520      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24521      * a cache of Roo.data.Records.
24522      */
24523     readRecords : function(doc){
24524         /**
24525          * After any data loads/reads, the raw XML Document is available for further custom processing.
24526          * @type XMLDocument
24527          */
24528         this.xmlData = doc;
24529         var root = doc.documentElement || doc;
24530         var q = Roo.DomQuery;
24531         var recordType = this.recordType, fields = recordType.prototype.fields;
24532         var sid = this.meta.id;
24533         var totalRecords = 0, success = true;
24534         if(this.meta.totalRecords){
24535             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24536         }
24537         
24538         if(this.meta.success){
24539             var sv = q.selectValue(this.meta.success, root, true);
24540             success = sv !== false && sv !== 'false';
24541         }
24542         var records = [];
24543         var ns = q.select(this.meta.record, root);
24544         for(var i = 0, len = ns.length; i < len; i++) {
24545                 var n = ns[i];
24546                 var values = {};
24547                 var id = sid ? q.selectValue(sid, n) : undefined;
24548                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24549                     var f = fields.items[j];
24550                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24551                     v = f.convert(v);
24552                     values[f.name] = v;
24553                 }
24554                 var record = new recordType(values, id);
24555                 record.node = n;
24556                 records[records.length] = record;
24557             }
24558
24559             return {
24560                 success : success,
24561                 records : records,
24562                 totalRecords : totalRecords || records.length
24563             };
24564     }
24565 });/*
24566  * Based on:
24567  * Ext JS Library 1.1.1
24568  * Copyright(c) 2006-2007, Ext JS, LLC.
24569  *
24570  * Originally Released Under LGPL - original licence link has changed is not relivant.
24571  *
24572  * Fork - LGPL
24573  * <script type="text/javascript">
24574  */
24575
24576 /**
24577  * @class Roo.data.ArrayReader
24578  * @extends Roo.data.DataReader
24579  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24580  * Each element of that Array represents a row of data fields. The
24581  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24582  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24583  * <p>
24584  * Example code:.
24585  * <pre><code>
24586 var RecordDef = Roo.data.Record.create([
24587     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24588     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24589 ]);
24590 var myReader = new Roo.data.ArrayReader({
24591     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24592 }, RecordDef);
24593 </code></pre>
24594  * <p>
24595  * This would consume an Array like this:
24596  * <pre><code>
24597 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24598   </code></pre>
24599  
24600  * @constructor
24601  * Create a new JsonReader
24602  * @param {Object} meta Metadata configuration options.
24603  * @param {Object|Array} recordType Either an Array of field definition objects
24604  * 
24605  * @cfg {Array} fields Array of field definition objects
24606  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24607  * as specified to {@link Roo.data.Record#create},
24608  * or an {@link Roo.data.Record} object
24609  *
24610  * 
24611  * created using {@link Roo.data.Record#create}.
24612  */
24613 Roo.data.ArrayReader = function(meta, recordType){
24614     
24615      
24616     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24617 };
24618
24619 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24620     /**
24621      * Create a data block containing Roo.data.Records from an XML document.
24622      * @param {Object} o An Array of row objects which represents the dataset.
24623      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24624      * a cache of Roo.data.Records.
24625      */
24626     readRecords : function(o)
24627     {
24628         var sid = this.meta ? this.meta.id : null;
24629         var recordType = this.recordType, fields = recordType.prototype.fields;
24630         var records = [];
24631         var root = o;
24632         for(var i = 0; i < root.length; i++){
24633                 var n = root[i];
24634             var values = {};
24635             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24636             for(var j = 0, jlen = fields.length; j < jlen; j++){
24637                 var f = fields.items[j];
24638                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24639                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24640                 v = f.convert(v);
24641                 values[f.name] = v;
24642             }
24643             var record = new recordType(values, id);
24644             record.json = n;
24645             records[records.length] = record;
24646         }
24647         return {
24648             records : records,
24649             totalRecords : records.length
24650         };
24651     }
24652 });/*
24653  * Based on:
24654  * Ext JS Library 1.1.1
24655  * Copyright(c) 2006-2007, Ext JS, LLC.
24656  *
24657  * Originally Released Under LGPL - original licence link has changed is not relivant.
24658  *
24659  * Fork - LGPL
24660  * <script type="text/javascript">
24661  */
24662
24663
24664 /**
24665  * @class Roo.data.Tree
24666  * @extends Roo.util.Observable
24667  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24668  * in the tree have most standard DOM functionality.
24669  * @constructor
24670  * @param {Node} root (optional) The root node
24671  */
24672 Roo.data.Tree = function(root){
24673    this.nodeHash = {};
24674    /**
24675     * The root node for this tree
24676     * @type Node
24677     */
24678    this.root = null;
24679    if(root){
24680        this.setRootNode(root);
24681    }
24682    this.addEvents({
24683        /**
24684         * @event append
24685         * Fires when a new child node is appended to a node in this tree.
24686         * @param {Tree} tree The owner tree
24687         * @param {Node} parent The parent node
24688         * @param {Node} node The newly appended node
24689         * @param {Number} index The index of the newly appended node
24690         */
24691        "append" : true,
24692        /**
24693         * @event remove
24694         * Fires when a child node is removed from a node in this tree.
24695         * @param {Tree} tree The owner tree
24696         * @param {Node} parent The parent node
24697         * @param {Node} node The child node removed
24698         */
24699        "remove" : true,
24700        /**
24701         * @event move
24702         * Fires when a node is moved to a new location in the tree
24703         * @param {Tree} tree The owner tree
24704         * @param {Node} node The node moved
24705         * @param {Node} oldParent The old parent of this node
24706         * @param {Node} newParent The new parent of this node
24707         * @param {Number} index The index it was moved to
24708         */
24709        "move" : true,
24710        /**
24711         * @event insert
24712         * Fires when a new child node is inserted in a node in this tree.
24713         * @param {Tree} tree The owner tree
24714         * @param {Node} parent The parent node
24715         * @param {Node} node The child node inserted
24716         * @param {Node} refNode The child node the node was inserted before
24717         */
24718        "insert" : true,
24719        /**
24720         * @event beforeappend
24721         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24722         * @param {Tree} tree The owner tree
24723         * @param {Node} parent The parent node
24724         * @param {Node} node The child node to be appended
24725         */
24726        "beforeappend" : true,
24727        /**
24728         * @event beforeremove
24729         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24730         * @param {Tree} tree The owner tree
24731         * @param {Node} parent The parent node
24732         * @param {Node} node The child node to be removed
24733         */
24734        "beforeremove" : true,
24735        /**
24736         * @event beforemove
24737         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24738         * @param {Tree} tree The owner tree
24739         * @param {Node} node The node being moved
24740         * @param {Node} oldParent The parent of the node
24741         * @param {Node} newParent The new parent the node is moving to
24742         * @param {Number} index The index it is being moved to
24743         */
24744        "beforemove" : true,
24745        /**
24746         * @event beforeinsert
24747         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} parent The parent node
24750         * @param {Node} node The child node to be inserted
24751         * @param {Node} refNode The child node the node is being inserted before
24752         */
24753        "beforeinsert" : true
24754    });
24755
24756     Roo.data.Tree.superclass.constructor.call(this);
24757 };
24758
24759 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24760     pathSeparator: "/",
24761
24762     proxyNodeEvent : function(){
24763         return this.fireEvent.apply(this, arguments);
24764     },
24765
24766     /**
24767      * Returns the root node for this tree.
24768      * @return {Node}
24769      */
24770     getRootNode : function(){
24771         return this.root;
24772     },
24773
24774     /**
24775      * Sets the root node for this tree.
24776      * @param {Node} node
24777      * @return {Node}
24778      */
24779     setRootNode : function(node){
24780         this.root = node;
24781         node.ownerTree = this;
24782         node.isRoot = true;
24783         this.registerNode(node);
24784         return node;
24785     },
24786
24787     /**
24788      * Gets a node in this tree by its id.
24789      * @param {String} id
24790      * @return {Node}
24791      */
24792     getNodeById : function(id){
24793         return this.nodeHash[id];
24794     },
24795
24796     registerNode : function(node){
24797         this.nodeHash[node.id] = node;
24798     },
24799
24800     unregisterNode : function(node){
24801         delete this.nodeHash[node.id];
24802     },
24803
24804     toString : function(){
24805         return "[Tree"+(this.id?" "+this.id:"")+"]";
24806     }
24807 });
24808
24809 /**
24810  * @class Roo.data.Node
24811  * @extends Roo.util.Observable
24812  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24813  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24814  * @constructor
24815  * @param {Object} attributes The attributes/config for the node
24816  */
24817 Roo.data.Node = function(attributes){
24818     /**
24819      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24820      * @type {Object}
24821      */
24822     this.attributes = attributes || {};
24823     this.leaf = this.attributes.leaf;
24824     /**
24825      * The node id. @type String
24826      */
24827     this.id = this.attributes.id;
24828     if(!this.id){
24829         this.id = Roo.id(null, "ynode-");
24830         this.attributes.id = this.id;
24831     }
24832      
24833     
24834     /**
24835      * All child nodes of this node. @type Array
24836      */
24837     this.childNodes = [];
24838     if(!this.childNodes.indexOf){ // indexOf is a must
24839         this.childNodes.indexOf = function(o){
24840             for(var i = 0, len = this.length; i < len; i++){
24841                 if(this[i] == o) {
24842                     return i;
24843                 }
24844             }
24845             return -1;
24846         };
24847     }
24848     /**
24849      * The parent node for this node. @type Node
24850      */
24851     this.parentNode = null;
24852     /**
24853      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24854      */
24855     this.firstChild = null;
24856     /**
24857      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24858      */
24859     this.lastChild = null;
24860     /**
24861      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24862      */
24863     this.previousSibling = null;
24864     /**
24865      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24866      */
24867     this.nextSibling = null;
24868
24869     this.addEvents({
24870        /**
24871         * @event append
24872         * Fires when a new child node is appended
24873         * @param {Tree} tree The owner tree
24874         * @param {Node} this This node
24875         * @param {Node} node The newly appended node
24876         * @param {Number} index The index of the newly appended node
24877         */
24878        "append" : true,
24879        /**
24880         * @event remove
24881         * Fires when a child node is removed
24882         * @param {Tree} tree The owner tree
24883         * @param {Node} this This node
24884         * @param {Node} node The removed node
24885         */
24886        "remove" : true,
24887        /**
24888         * @event move
24889         * Fires when this node is moved to a new location in the tree
24890         * @param {Tree} tree The owner tree
24891         * @param {Node} this This node
24892         * @param {Node} oldParent The old parent of this node
24893         * @param {Node} newParent The new parent of this node
24894         * @param {Number} index The index it was moved to
24895         */
24896        "move" : true,
24897        /**
24898         * @event insert
24899         * Fires when a new child node is inserted.
24900         * @param {Tree} tree The owner tree
24901         * @param {Node} this This node
24902         * @param {Node} node The child node inserted
24903         * @param {Node} refNode The child node the node was inserted before
24904         */
24905        "insert" : true,
24906        /**
24907         * @event beforeappend
24908         * Fires before a new child is appended, return false to cancel the append.
24909         * @param {Tree} tree The owner tree
24910         * @param {Node} this This node
24911         * @param {Node} node The child node to be appended
24912         */
24913        "beforeappend" : true,
24914        /**
24915         * @event beforeremove
24916         * Fires before a child is removed, return false to cancel the remove.
24917         * @param {Tree} tree The owner tree
24918         * @param {Node} this This node
24919         * @param {Node} node The child node to be removed
24920         */
24921        "beforeremove" : true,
24922        /**
24923         * @event beforemove
24924         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24925         * @param {Tree} tree The owner tree
24926         * @param {Node} this This node
24927         * @param {Node} oldParent The parent of this node
24928         * @param {Node} newParent The new parent this node is moving to
24929         * @param {Number} index The index it is being moved to
24930         */
24931        "beforemove" : true,
24932        /**
24933         * @event beforeinsert
24934         * Fires before a new child is inserted, return false to cancel the insert.
24935         * @param {Tree} tree The owner tree
24936         * @param {Node} this This node
24937         * @param {Node} node The child node to be inserted
24938         * @param {Node} refNode The child node the node is being inserted before
24939         */
24940        "beforeinsert" : true
24941    });
24942     this.listeners = this.attributes.listeners;
24943     Roo.data.Node.superclass.constructor.call(this);
24944 };
24945
24946 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24947     fireEvent : function(evtName){
24948         // first do standard event for this node
24949         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24950             return false;
24951         }
24952         // then bubble it up to the tree if the event wasn't cancelled
24953         var ot = this.getOwnerTree();
24954         if(ot){
24955             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24956                 return false;
24957             }
24958         }
24959         return true;
24960     },
24961
24962     /**
24963      * Returns true if this node is a leaf
24964      * @return {Boolean}
24965      */
24966     isLeaf : function(){
24967         return this.leaf === true;
24968     },
24969
24970     // private
24971     setFirstChild : function(node){
24972         this.firstChild = node;
24973     },
24974
24975     //private
24976     setLastChild : function(node){
24977         this.lastChild = node;
24978     },
24979
24980
24981     /**
24982      * Returns true if this node is the last child of its parent
24983      * @return {Boolean}
24984      */
24985     isLast : function(){
24986        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24987     },
24988
24989     /**
24990      * Returns true if this node is the first child of its parent
24991      * @return {Boolean}
24992      */
24993     isFirst : function(){
24994        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24995     },
24996
24997     hasChildNodes : function(){
24998         return !this.isLeaf() && this.childNodes.length > 0;
24999     },
25000
25001     /**
25002      * Insert node(s) as the last child node of this node.
25003      * @param {Node/Array} node The node or Array of nodes to append
25004      * @return {Node} The appended node if single append, or null if an array was passed
25005      */
25006     appendChild : function(node){
25007         var multi = false;
25008         if(node instanceof Array){
25009             multi = node;
25010         }else if(arguments.length > 1){
25011             multi = arguments;
25012         }
25013         
25014         // if passed an array or multiple args do them one by one
25015         if(multi){
25016             for(var i = 0, len = multi.length; i < len; i++) {
25017                 this.appendChild(multi[i]);
25018             }
25019         }else{
25020             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25021                 return false;
25022             }
25023             var index = this.childNodes.length;
25024             var oldParent = node.parentNode;
25025             // it's a move, make sure we move it cleanly
25026             if(oldParent){
25027                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25028                     return false;
25029                 }
25030                 oldParent.removeChild(node);
25031             }
25032             
25033             index = this.childNodes.length;
25034             if(index == 0){
25035                 this.setFirstChild(node);
25036             }
25037             this.childNodes.push(node);
25038             node.parentNode = this;
25039             var ps = this.childNodes[index-1];
25040             if(ps){
25041                 node.previousSibling = ps;
25042                 ps.nextSibling = node;
25043             }else{
25044                 node.previousSibling = null;
25045             }
25046             node.nextSibling = null;
25047             this.setLastChild(node);
25048             node.setOwnerTree(this.getOwnerTree());
25049             this.fireEvent("append", this.ownerTree, this, node, index);
25050             if(this.ownerTree) {
25051                 this.ownerTree.fireEvent("appendnode", this, node, index);
25052             }
25053             if(oldParent){
25054                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25055             }
25056             return node;
25057         }
25058     },
25059
25060     /**
25061      * Removes a child node from this node.
25062      * @param {Node} node The node to remove
25063      * @return {Node} The removed node
25064      */
25065     removeChild : function(node){
25066         var index = this.childNodes.indexOf(node);
25067         if(index == -1){
25068             return false;
25069         }
25070         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25071             return false;
25072         }
25073
25074         // remove it from childNodes collection
25075         this.childNodes.splice(index, 1);
25076
25077         // update siblings
25078         if(node.previousSibling){
25079             node.previousSibling.nextSibling = node.nextSibling;
25080         }
25081         if(node.nextSibling){
25082             node.nextSibling.previousSibling = node.previousSibling;
25083         }
25084
25085         // update child refs
25086         if(this.firstChild == node){
25087             this.setFirstChild(node.nextSibling);
25088         }
25089         if(this.lastChild == node){
25090             this.setLastChild(node.previousSibling);
25091         }
25092
25093         node.setOwnerTree(null);
25094         // clear any references from the node
25095         node.parentNode = null;
25096         node.previousSibling = null;
25097         node.nextSibling = null;
25098         this.fireEvent("remove", this.ownerTree, this, node);
25099         return node;
25100     },
25101
25102     /**
25103      * Inserts the first node before the second node in this nodes childNodes collection.
25104      * @param {Node} node The node to insert
25105      * @param {Node} refNode The node to insert before (if null the node is appended)
25106      * @return {Node} The inserted node
25107      */
25108     insertBefore : function(node, refNode){
25109         if(!refNode){ // like standard Dom, refNode can be null for append
25110             return this.appendChild(node);
25111         }
25112         // nothing to do
25113         if(node == refNode){
25114             return false;
25115         }
25116
25117         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25118             return false;
25119         }
25120         var index = this.childNodes.indexOf(refNode);
25121         var oldParent = node.parentNode;
25122         var refIndex = index;
25123
25124         // when moving internally, indexes will change after remove
25125         if(oldParent == this && this.childNodes.indexOf(node) < index){
25126             refIndex--;
25127         }
25128
25129         // it's a move, make sure we move it cleanly
25130         if(oldParent){
25131             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25132                 return false;
25133             }
25134             oldParent.removeChild(node);
25135         }
25136         if(refIndex == 0){
25137             this.setFirstChild(node);
25138         }
25139         this.childNodes.splice(refIndex, 0, node);
25140         node.parentNode = this;
25141         var ps = this.childNodes[refIndex-1];
25142         if(ps){
25143             node.previousSibling = ps;
25144             ps.nextSibling = node;
25145         }else{
25146             node.previousSibling = null;
25147         }
25148         node.nextSibling = refNode;
25149         refNode.previousSibling = node;
25150         node.setOwnerTree(this.getOwnerTree());
25151         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25152         if(oldParent){
25153             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25154         }
25155         return node;
25156     },
25157
25158     /**
25159      * Returns the child node at the specified index.
25160      * @param {Number} index
25161      * @return {Node}
25162      */
25163     item : function(index){
25164         return this.childNodes[index];
25165     },
25166
25167     /**
25168      * Replaces one child node in this node with another.
25169      * @param {Node} newChild The replacement node
25170      * @param {Node} oldChild The node to replace
25171      * @return {Node} The replaced node
25172      */
25173     replaceChild : function(newChild, oldChild){
25174         this.insertBefore(newChild, oldChild);
25175         this.removeChild(oldChild);
25176         return oldChild;
25177     },
25178
25179     /**
25180      * Returns the index of a child node
25181      * @param {Node} node
25182      * @return {Number} The index of the node or -1 if it was not found
25183      */
25184     indexOf : function(child){
25185         return this.childNodes.indexOf(child);
25186     },
25187
25188     /**
25189      * Returns the tree this node is in.
25190      * @return {Tree}
25191      */
25192     getOwnerTree : function(){
25193         // if it doesn't have one, look for one
25194         if(!this.ownerTree){
25195             var p = this;
25196             while(p){
25197                 if(p.ownerTree){
25198                     this.ownerTree = p.ownerTree;
25199                     break;
25200                 }
25201                 p = p.parentNode;
25202             }
25203         }
25204         return this.ownerTree;
25205     },
25206
25207     /**
25208      * Returns depth of this node (the root node has a depth of 0)
25209      * @return {Number}
25210      */
25211     getDepth : function(){
25212         var depth = 0;
25213         var p = this;
25214         while(p.parentNode){
25215             ++depth;
25216             p = p.parentNode;
25217         }
25218         return depth;
25219     },
25220
25221     // private
25222     setOwnerTree : function(tree){
25223         // if it's move, we need to update everyone
25224         if(tree != this.ownerTree){
25225             if(this.ownerTree){
25226                 this.ownerTree.unregisterNode(this);
25227             }
25228             this.ownerTree = tree;
25229             var cs = this.childNodes;
25230             for(var i = 0, len = cs.length; i < len; i++) {
25231                 cs[i].setOwnerTree(tree);
25232             }
25233             if(tree){
25234                 tree.registerNode(this);
25235             }
25236         }
25237     },
25238
25239     /**
25240      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25241      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25242      * @return {String} The path
25243      */
25244     getPath : function(attr){
25245         attr = attr || "id";
25246         var p = this.parentNode;
25247         var b = [this.attributes[attr]];
25248         while(p){
25249             b.unshift(p.attributes[attr]);
25250             p = p.parentNode;
25251         }
25252         var sep = this.getOwnerTree().pathSeparator;
25253         return sep + b.join(sep);
25254     },
25255
25256     /**
25257      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25258      * function call will be the scope provided or the current node. The arguments to the function
25259      * will be the args provided or the current node. If the function returns false at any point,
25260      * the bubble is stopped.
25261      * @param {Function} fn The function to call
25262      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25263      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25264      */
25265     bubble : function(fn, scope, args){
25266         var p = this;
25267         while(p){
25268             if(fn.call(scope || p, args || p) === false){
25269                 break;
25270             }
25271             p = p.parentNode;
25272         }
25273     },
25274
25275     /**
25276      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25277      * function call will be the scope provided or the current node. The arguments to the function
25278      * will be the args provided or the current node. If the function returns false at any point,
25279      * the cascade is stopped on that branch.
25280      * @param {Function} fn The function to call
25281      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25282      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25283      */
25284     cascade : function(fn, scope, args){
25285         if(fn.call(scope || this, args || this) !== false){
25286             var cs = this.childNodes;
25287             for(var i = 0, len = cs.length; i < len; i++) {
25288                 cs[i].cascade(fn, scope, args);
25289             }
25290         }
25291     },
25292
25293     /**
25294      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25295      * function call will be the scope provided or the current node. The arguments to the function
25296      * will be the args provided or the current node. If the function returns false at any point,
25297      * the iteration stops.
25298      * @param {Function} fn The function to call
25299      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25300      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25301      */
25302     eachChild : function(fn, scope, args){
25303         var cs = this.childNodes;
25304         for(var i = 0, len = cs.length; i < len; i++) {
25305                 if(fn.call(scope || this, args || cs[i]) === false){
25306                     break;
25307                 }
25308         }
25309     },
25310
25311     /**
25312      * Finds the first child that has the attribute with the specified value.
25313      * @param {String} attribute The attribute name
25314      * @param {Mixed} value The value to search for
25315      * @return {Node} The found child or null if none was found
25316      */
25317     findChild : function(attribute, value){
25318         var cs = this.childNodes;
25319         for(var i = 0, len = cs.length; i < len; i++) {
25320                 if(cs[i].attributes[attribute] == value){
25321                     return cs[i];
25322                 }
25323         }
25324         return null;
25325     },
25326
25327     /**
25328      * Finds the first child by a custom function. The child matches if the function passed
25329      * returns true.
25330      * @param {Function} fn
25331      * @param {Object} scope (optional)
25332      * @return {Node} The found child or null if none was found
25333      */
25334     findChildBy : function(fn, scope){
25335         var cs = this.childNodes;
25336         for(var i = 0, len = cs.length; i < len; i++) {
25337                 if(fn.call(scope||cs[i], cs[i]) === true){
25338                     return cs[i];
25339                 }
25340         }
25341         return null;
25342     },
25343
25344     /**
25345      * Sorts this nodes children using the supplied sort function
25346      * @param {Function} fn
25347      * @param {Object} scope (optional)
25348      */
25349     sort : function(fn, scope){
25350         var cs = this.childNodes;
25351         var len = cs.length;
25352         if(len > 0){
25353             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25354             cs.sort(sortFn);
25355             for(var i = 0; i < len; i++){
25356                 var n = cs[i];
25357                 n.previousSibling = cs[i-1];
25358                 n.nextSibling = cs[i+1];
25359                 if(i == 0){
25360                     this.setFirstChild(n);
25361                 }
25362                 if(i == len-1){
25363                     this.setLastChild(n);
25364                 }
25365             }
25366         }
25367     },
25368
25369     /**
25370      * Returns true if this node is an ancestor (at any point) of the passed node.
25371      * @param {Node} node
25372      * @return {Boolean}
25373      */
25374     contains : function(node){
25375         return node.isAncestor(this);
25376     },
25377
25378     /**
25379      * Returns true if the passed node is an ancestor (at any point) of this node.
25380      * @param {Node} node
25381      * @return {Boolean}
25382      */
25383     isAncestor : function(node){
25384         var p = this.parentNode;
25385         while(p){
25386             if(p == node){
25387                 return true;
25388             }
25389             p = p.parentNode;
25390         }
25391         return false;
25392     },
25393
25394     toString : function(){
25395         return "[Node"+(this.id?" "+this.id:"")+"]";
25396     }
25397 });/*
25398  * Based on:
25399  * Ext JS Library 1.1.1
25400  * Copyright(c) 2006-2007, Ext JS, LLC.
25401  *
25402  * Originally Released Under LGPL - original licence link has changed is not relivant.
25403  *
25404  * Fork - LGPL
25405  * <script type="text/javascript">
25406  */
25407  (function(){ 
25408 /**
25409  * @class Roo.Layer
25410  * @extends Roo.Element
25411  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25412  * automatic maintaining of shadow/shim positions.
25413  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25414  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25415  * you can pass a string with a CSS class name. False turns off the shadow.
25416  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25417  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25418  * @cfg {String} cls CSS class to add to the element
25419  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25420  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25421  * @constructor
25422  * @param {Object} config An object with config options.
25423  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25424  */
25425
25426 Roo.Layer = function(config, existingEl){
25427     config = config || {};
25428     var dh = Roo.DomHelper;
25429     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25430     if(existingEl){
25431         this.dom = Roo.getDom(existingEl);
25432     }
25433     if(!this.dom){
25434         var o = config.dh || {tag: "div", cls: "x-layer"};
25435         this.dom = dh.append(pel, o);
25436     }
25437     if(config.cls){
25438         this.addClass(config.cls);
25439     }
25440     this.constrain = config.constrain !== false;
25441     this.visibilityMode = Roo.Element.VISIBILITY;
25442     if(config.id){
25443         this.id = this.dom.id = config.id;
25444     }else{
25445         this.id = Roo.id(this.dom);
25446     }
25447     this.zindex = config.zindex || this.getZIndex();
25448     this.position("absolute", this.zindex);
25449     if(config.shadow){
25450         this.shadowOffset = config.shadowOffset || 4;
25451         this.shadow = new Roo.Shadow({
25452             offset : this.shadowOffset,
25453             mode : config.shadow
25454         });
25455     }else{
25456         this.shadowOffset = 0;
25457     }
25458     this.useShim = config.shim !== false && Roo.useShims;
25459     this.useDisplay = config.useDisplay;
25460     this.hide();
25461 };
25462
25463 var supr = Roo.Element.prototype;
25464
25465 // shims are shared among layer to keep from having 100 iframes
25466 var shims = [];
25467
25468 Roo.extend(Roo.Layer, Roo.Element, {
25469
25470     getZIndex : function(){
25471         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25472     },
25473
25474     getShim : function(){
25475         if(!this.useShim){
25476             return null;
25477         }
25478         if(this.shim){
25479             return this.shim;
25480         }
25481         var shim = shims.shift();
25482         if(!shim){
25483             shim = this.createShim();
25484             shim.enableDisplayMode('block');
25485             shim.dom.style.display = 'none';
25486             shim.dom.style.visibility = 'visible';
25487         }
25488         var pn = this.dom.parentNode;
25489         if(shim.dom.parentNode != pn){
25490             pn.insertBefore(shim.dom, this.dom);
25491         }
25492         shim.setStyle('z-index', this.getZIndex()-2);
25493         this.shim = shim;
25494         return shim;
25495     },
25496
25497     hideShim : function(){
25498         if(this.shim){
25499             this.shim.setDisplayed(false);
25500             shims.push(this.shim);
25501             delete this.shim;
25502         }
25503     },
25504
25505     disableShadow : function(){
25506         if(this.shadow){
25507             this.shadowDisabled = true;
25508             this.shadow.hide();
25509             this.lastShadowOffset = this.shadowOffset;
25510             this.shadowOffset = 0;
25511         }
25512     },
25513
25514     enableShadow : function(show){
25515         if(this.shadow){
25516             this.shadowDisabled = false;
25517             this.shadowOffset = this.lastShadowOffset;
25518             delete this.lastShadowOffset;
25519             if(show){
25520                 this.sync(true);
25521             }
25522         }
25523     },
25524
25525     // private
25526     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25527     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25528     sync : function(doShow){
25529         var sw = this.shadow;
25530         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25531             var sh = this.getShim();
25532
25533             var w = this.getWidth(),
25534                 h = this.getHeight();
25535
25536             var l = this.getLeft(true),
25537                 t = this.getTop(true);
25538
25539             if(sw && !this.shadowDisabled){
25540                 if(doShow && !sw.isVisible()){
25541                     sw.show(this);
25542                 }else{
25543                     sw.realign(l, t, w, h);
25544                 }
25545                 if(sh){
25546                     if(doShow){
25547                        sh.show();
25548                     }
25549                     // fit the shim behind the shadow, so it is shimmed too
25550                     var a = sw.adjusts, s = sh.dom.style;
25551                     s.left = (Math.min(l, l+a.l))+"px";
25552                     s.top = (Math.min(t, t+a.t))+"px";
25553                     s.width = (w+a.w)+"px";
25554                     s.height = (h+a.h)+"px";
25555                 }
25556             }else if(sh){
25557                 if(doShow){
25558                    sh.show();
25559                 }
25560                 sh.setSize(w, h);
25561                 sh.setLeftTop(l, t);
25562             }
25563             
25564         }
25565     },
25566
25567     // private
25568     destroy : function(){
25569         this.hideShim();
25570         if(this.shadow){
25571             this.shadow.hide();
25572         }
25573         this.removeAllListeners();
25574         var pn = this.dom.parentNode;
25575         if(pn){
25576             pn.removeChild(this.dom);
25577         }
25578         Roo.Element.uncache(this.id);
25579     },
25580
25581     remove : function(){
25582         this.destroy();
25583     },
25584
25585     // private
25586     beginUpdate : function(){
25587         this.updating = true;
25588     },
25589
25590     // private
25591     endUpdate : function(){
25592         this.updating = false;
25593         this.sync(true);
25594     },
25595
25596     // private
25597     hideUnders : function(negOffset){
25598         if(this.shadow){
25599             this.shadow.hide();
25600         }
25601         this.hideShim();
25602     },
25603
25604     // private
25605     constrainXY : function(){
25606         if(this.constrain){
25607             var vw = Roo.lib.Dom.getViewWidth(),
25608                 vh = Roo.lib.Dom.getViewHeight();
25609             var s = Roo.get(document).getScroll();
25610
25611             var xy = this.getXY();
25612             var x = xy[0], y = xy[1];   
25613             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25614             // only move it if it needs it
25615             var moved = false;
25616             // first validate right/bottom
25617             if((x + w) > vw+s.left){
25618                 x = vw - w - this.shadowOffset;
25619                 moved = true;
25620             }
25621             if((y + h) > vh+s.top){
25622                 y = vh - h - this.shadowOffset;
25623                 moved = true;
25624             }
25625             // then make sure top/left isn't negative
25626             if(x < s.left){
25627                 x = s.left;
25628                 moved = true;
25629             }
25630             if(y < s.top){
25631                 y = s.top;
25632                 moved = true;
25633             }
25634             if(moved){
25635                 if(this.avoidY){
25636                     var ay = this.avoidY;
25637                     if(y <= ay && (y+h) >= ay){
25638                         y = ay-h-5;   
25639                     }
25640                 }
25641                 xy = [x, y];
25642                 this.storeXY(xy);
25643                 supr.setXY.call(this, xy);
25644                 this.sync();
25645             }
25646         }
25647     },
25648
25649     isVisible : function(){
25650         return this.visible;    
25651     },
25652
25653     // private
25654     showAction : function(){
25655         this.visible = true; // track visibility to prevent getStyle calls
25656         if(this.useDisplay === true){
25657             this.setDisplayed("");
25658         }else if(this.lastXY){
25659             supr.setXY.call(this, this.lastXY);
25660         }else if(this.lastLT){
25661             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25662         }
25663     },
25664
25665     // private
25666     hideAction : function(){
25667         this.visible = false;
25668         if(this.useDisplay === true){
25669             this.setDisplayed(false);
25670         }else{
25671             this.setLeftTop(-10000,-10000);
25672         }
25673     },
25674
25675     // overridden Element method
25676     setVisible : function(v, a, d, c, e){
25677         if(v){
25678             this.showAction();
25679         }
25680         if(a && v){
25681             var cb = function(){
25682                 this.sync(true);
25683                 if(c){
25684                     c();
25685                 }
25686             }.createDelegate(this);
25687             supr.setVisible.call(this, true, true, d, cb, e);
25688         }else{
25689             if(!v){
25690                 this.hideUnders(true);
25691             }
25692             var cb = c;
25693             if(a){
25694                 cb = function(){
25695                     this.hideAction();
25696                     if(c){
25697                         c();
25698                     }
25699                 }.createDelegate(this);
25700             }
25701             supr.setVisible.call(this, v, a, d, cb, e);
25702             if(v){
25703                 this.sync(true);
25704             }else if(!a){
25705                 this.hideAction();
25706             }
25707         }
25708     },
25709
25710     storeXY : function(xy){
25711         delete this.lastLT;
25712         this.lastXY = xy;
25713     },
25714
25715     storeLeftTop : function(left, top){
25716         delete this.lastXY;
25717         this.lastLT = [left, top];
25718     },
25719
25720     // private
25721     beforeFx : function(){
25722         this.beforeAction();
25723         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25724     },
25725
25726     // private
25727     afterFx : function(){
25728         Roo.Layer.superclass.afterFx.apply(this, arguments);
25729         this.sync(this.isVisible());
25730     },
25731
25732     // private
25733     beforeAction : function(){
25734         if(!this.updating && this.shadow){
25735             this.shadow.hide();
25736         }
25737     },
25738
25739     // overridden Element method
25740     setLeft : function(left){
25741         this.storeLeftTop(left, this.getTop(true));
25742         supr.setLeft.apply(this, arguments);
25743         this.sync();
25744     },
25745
25746     setTop : function(top){
25747         this.storeLeftTop(this.getLeft(true), top);
25748         supr.setTop.apply(this, arguments);
25749         this.sync();
25750     },
25751
25752     setLeftTop : function(left, top){
25753         this.storeLeftTop(left, top);
25754         supr.setLeftTop.apply(this, arguments);
25755         this.sync();
25756     },
25757
25758     setXY : function(xy, a, d, c, e){
25759         this.fixDisplay();
25760         this.beforeAction();
25761         this.storeXY(xy);
25762         var cb = this.createCB(c);
25763         supr.setXY.call(this, xy, a, d, cb, e);
25764         if(!a){
25765             cb();
25766         }
25767     },
25768
25769     // private
25770     createCB : function(c){
25771         var el = this;
25772         return function(){
25773             el.constrainXY();
25774             el.sync(true);
25775             if(c){
25776                 c();
25777             }
25778         };
25779     },
25780
25781     // overridden Element method
25782     setX : function(x, a, d, c, e){
25783         this.setXY([x, this.getY()], a, d, c, e);
25784     },
25785
25786     // overridden Element method
25787     setY : function(y, a, d, c, e){
25788         this.setXY([this.getX(), y], a, d, c, e);
25789     },
25790
25791     // overridden Element method
25792     setSize : function(w, h, a, d, c, e){
25793         this.beforeAction();
25794         var cb = this.createCB(c);
25795         supr.setSize.call(this, w, h, a, d, cb, e);
25796         if(!a){
25797             cb();
25798         }
25799     },
25800
25801     // overridden Element method
25802     setWidth : function(w, a, d, c, e){
25803         this.beforeAction();
25804         var cb = this.createCB(c);
25805         supr.setWidth.call(this, w, a, d, cb, e);
25806         if(!a){
25807             cb();
25808         }
25809     },
25810
25811     // overridden Element method
25812     setHeight : function(h, a, d, c, e){
25813         this.beforeAction();
25814         var cb = this.createCB(c);
25815         supr.setHeight.call(this, h, a, d, cb, e);
25816         if(!a){
25817             cb();
25818         }
25819     },
25820
25821     // overridden Element method
25822     setBounds : function(x, y, w, h, a, d, c, e){
25823         this.beforeAction();
25824         var cb = this.createCB(c);
25825         if(!a){
25826             this.storeXY([x, y]);
25827             supr.setXY.call(this, [x, y]);
25828             supr.setSize.call(this, w, h, a, d, cb, e);
25829             cb();
25830         }else{
25831             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25832         }
25833         return this;
25834     },
25835     
25836     /**
25837      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25838      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25839      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25840      * @param {Number} zindex The new z-index to set
25841      * @return {this} The Layer
25842      */
25843     setZIndex : function(zindex){
25844         this.zindex = zindex;
25845         this.setStyle("z-index", zindex + 2);
25846         if(this.shadow){
25847             this.shadow.setZIndex(zindex + 1);
25848         }
25849         if(this.shim){
25850             this.shim.setStyle("z-index", zindex);
25851         }
25852     }
25853 });
25854 })();/*
25855  * Based on:
25856  * Ext JS Library 1.1.1
25857  * Copyright(c) 2006-2007, Ext JS, LLC.
25858  *
25859  * Originally Released Under LGPL - original licence link has changed is not relivant.
25860  *
25861  * Fork - LGPL
25862  * <script type="text/javascript">
25863  */
25864
25865
25866 /**
25867  * @class Roo.Shadow
25868  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25869  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25870  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25871  * @constructor
25872  * Create a new Shadow
25873  * @param {Object} config The config object
25874  */
25875 Roo.Shadow = function(config){
25876     Roo.apply(this, config);
25877     if(typeof this.mode != "string"){
25878         this.mode = this.defaultMode;
25879     }
25880     var o = this.offset, a = {h: 0};
25881     var rad = Math.floor(this.offset/2);
25882     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25883         case "drop":
25884             a.w = 0;
25885             a.l = a.t = o;
25886             a.t -= 1;
25887             if(Roo.isIE){
25888                 a.l -= this.offset + rad;
25889                 a.t -= this.offset + rad;
25890                 a.w -= rad;
25891                 a.h -= rad;
25892                 a.t += 1;
25893             }
25894         break;
25895         case "sides":
25896             a.w = (o*2);
25897             a.l = -o;
25898             a.t = o-1;
25899             if(Roo.isIE){
25900                 a.l -= (this.offset - rad);
25901                 a.t -= this.offset + rad;
25902                 a.l += 1;
25903                 a.w -= (this.offset - rad)*2;
25904                 a.w -= rad + 1;
25905                 a.h -= 1;
25906             }
25907         break;
25908         case "frame":
25909             a.w = a.h = (o*2);
25910             a.l = a.t = -o;
25911             a.t += 1;
25912             a.h -= 2;
25913             if(Roo.isIE){
25914                 a.l -= (this.offset - rad);
25915                 a.t -= (this.offset - rad);
25916                 a.l += 1;
25917                 a.w -= (this.offset + rad + 1);
25918                 a.h -= (this.offset + rad);
25919                 a.h += 1;
25920             }
25921         break;
25922     };
25923
25924     this.adjusts = a;
25925 };
25926
25927 Roo.Shadow.prototype = {
25928     /**
25929      * @cfg {String} mode
25930      * The shadow display mode.  Supports the following options:<br />
25931      * sides: Shadow displays on both sides and bottom only<br />
25932      * frame: Shadow displays equally on all four sides<br />
25933      * drop: Traditional bottom-right drop shadow (default)
25934      */
25935     /**
25936      * @cfg {String} offset
25937      * The number of pixels to offset the shadow from the element (defaults to 4)
25938      */
25939     offset: 4,
25940
25941     // private
25942     defaultMode: "drop",
25943
25944     /**
25945      * Displays the shadow under the target element
25946      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25947      */
25948     show : function(target){
25949         target = Roo.get(target);
25950         if(!this.el){
25951             this.el = Roo.Shadow.Pool.pull();
25952             if(this.el.dom.nextSibling != target.dom){
25953                 this.el.insertBefore(target);
25954             }
25955         }
25956         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25957         if(Roo.isIE){
25958             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25959         }
25960         this.realign(
25961             target.getLeft(true),
25962             target.getTop(true),
25963             target.getWidth(),
25964             target.getHeight()
25965         );
25966         this.el.dom.style.display = "block";
25967     },
25968
25969     /**
25970      * Returns true if the shadow is visible, else false
25971      */
25972     isVisible : function(){
25973         return this.el ? true : false;  
25974     },
25975
25976     /**
25977      * Direct alignment when values are already available. Show must be called at least once before
25978      * calling this method to ensure it is initialized.
25979      * @param {Number} left The target element left position
25980      * @param {Number} top The target element top position
25981      * @param {Number} width The target element width
25982      * @param {Number} height The target element height
25983      */
25984     realign : function(l, t, w, h){
25985         if(!this.el){
25986             return;
25987         }
25988         var a = this.adjusts, d = this.el.dom, s = d.style;
25989         var iea = 0;
25990         s.left = (l+a.l)+"px";
25991         s.top = (t+a.t)+"px";
25992         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25993  
25994         if(s.width != sws || s.height != shs){
25995             s.width = sws;
25996             s.height = shs;
25997             if(!Roo.isIE){
25998                 var cn = d.childNodes;
25999                 var sww = Math.max(0, (sw-12))+"px";
26000                 cn[0].childNodes[1].style.width = sww;
26001                 cn[1].childNodes[1].style.width = sww;
26002                 cn[2].childNodes[1].style.width = sww;
26003                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26004             }
26005         }
26006     },
26007
26008     /**
26009      * Hides this shadow
26010      */
26011     hide : function(){
26012         if(this.el){
26013             this.el.dom.style.display = "none";
26014             Roo.Shadow.Pool.push(this.el);
26015             delete this.el;
26016         }
26017     },
26018
26019     /**
26020      * Adjust the z-index of this shadow
26021      * @param {Number} zindex The new z-index
26022      */
26023     setZIndex : function(z){
26024         this.zIndex = z;
26025         if(this.el){
26026             this.el.setStyle("z-index", z);
26027         }
26028     }
26029 };
26030
26031 // Private utility class that manages the internal Shadow cache
26032 Roo.Shadow.Pool = function(){
26033     var p = [];
26034     var markup = Roo.isIE ?
26035                  '<div class="x-ie-shadow"></div>' :
26036                  '<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>';
26037     return {
26038         pull : function(){
26039             var sh = p.shift();
26040             if(!sh){
26041                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26042                 sh.autoBoxAdjust = false;
26043             }
26044             return sh;
26045         },
26046
26047         push : function(sh){
26048             p.push(sh);
26049         }
26050     };
26051 }();/*
26052  * Based on:
26053  * Ext JS Library 1.1.1
26054  * Copyright(c) 2006-2007, Ext JS, LLC.
26055  *
26056  * Originally Released Under LGPL - original licence link has changed is not relivant.
26057  *
26058  * Fork - LGPL
26059  * <script type="text/javascript">
26060  */
26061
26062
26063 /**
26064  * @class Roo.SplitBar
26065  * @extends Roo.util.Observable
26066  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26067  * <br><br>
26068  * Usage:
26069  * <pre><code>
26070 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26071                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26072 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26073 split.minSize = 100;
26074 split.maxSize = 600;
26075 split.animate = true;
26076 split.on('moved', splitterMoved);
26077 </code></pre>
26078  * @constructor
26079  * Create a new SplitBar
26080  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26081  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26082  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26083  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26084                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26085                         position of the SplitBar).
26086  */
26087 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26088     
26089     /** @private */
26090     this.el = Roo.get(dragElement, true);
26091     this.el.dom.unselectable = "on";
26092     /** @private */
26093     this.resizingEl = Roo.get(resizingElement, true);
26094
26095     /**
26096      * @private
26097      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26098      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26099      * @type Number
26100      */
26101     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26102     
26103     /**
26104      * The minimum size of the resizing element. (Defaults to 0)
26105      * @type Number
26106      */
26107     this.minSize = 0;
26108     
26109     /**
26110      * The maximum size of the resizing element. (Defaults to 2000)
26111      * @type Number
26112      */
26113     this.maxSize = 2000;
26114     
26115     /**
26116      * Whether to animate the transition to the new size
26117      * @type Boolean
26118      */
26119     this.animate = false;
26120     
26121     /**
26122      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26123      * @type Boolean
26124      */
26125     this.useShim = false;
26126     
26127     /** @private */
26128     this.shim = null;
26129     
26130     if(!existingProxy){
26131         /** @private */
26132         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26133     }else{
26134         this.proxy = Roo.get(existingProxy).dom;
26135     }
26136     /** @private */
26137     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26138     
26139     /** @private */
26140     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26141     
26142     /** @private */
26143     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26144     
26145     /** @private */
26146     this.dragSpecs = {};
26147     
26148     /**
26149      * @private The adapter to use to positon and resize elements
26150      */
26151     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26152     this.adapter.init(this);
26153     
26154     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26155         /** @private */
26156         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26157         this.el.addClass("x-splitbar-h");
26158     }else{
26159         /** @private */
26160         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26161         this.el.addClass("x-splitbar-v");
26162     }
26163     
26164     this.addEvents({
26165         /**
26166          * @event resize
26167          * Fires when the splitter is moved (alias for {@link #event-moved})
26168          * @param {Roo.SplitBar} this
26169          * @param {Number} newSize the new width or height
26170          */
26171         "resize" : true,
26172         /**
26173          * @event moved
26174          * Fires when the splitter is moved
26175          * @param {Roo.SplitBar} this
26176          * @param {Number} newSize the new width or height
26177          */
26178         "moved" : true,
26179         /**
26180          * @event beforeresize
26181          * Fires before the splitter is dragged
26182          * @param {Roo.SplitBar} this
26183          */
26184         "beforeresize" : true,
26185
26186         "beforeapply" : true
26187     });
26188
26189     Roo.util.Observable.call(this);
26190 };
26191
26192 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26193     onStartProxyDrag : function(x, y){
26194         this.fireEvent("beforeresize", this);
26195         if(!this.overlay){
26196             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26197             o.unselectable();
26198             o.enableDisplayMode("block");
26199             // all splitbars share the same overlay
26200             Roo.SplitBar.prototype.overlay = o;
26201         }
26202         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26203         this.overlay.show();
26204         Roo.get(this.proxy).setDisplayed("block");
26205         var size = this.adapter.getElementSize(this);
26206         this.activeMinSize = this.getMinimumSize();;
26207         this.activeMaxSize = this.getMaximumSize();;
26208         var c1 = size - this.activeMinSize;
26209         var c2 = Math.max(this.activeMaxSize - size, 0);
26210         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26211             this.dd.resetConstraints();
26212             this.dd.setXConstraint(
26213                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26214                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26215             );
26216             this.dd.setYConstraint(0, 0);
26217         }else{
26218             this.dd.resetConstraints();
26219             this.dd.setXConstraint(0, 0);
26220             this.dd.setYConstraint(
26221                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26222                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26223             );
26224          }
26225         this.dragSpecs.startSize = size;
26226         this.dragSpecs.startPoint = [x, y];
26227         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26228     },
26229     
26230     /** 
26231      * @private Called after the drag operation by the DDProxy
26232      */
26233     onEndProxyDrag : function(e){
26234         Roo.get(this.proxy).setDisplayed(false);
26235         var endPoint = Roo.lib.Event.getXY(e);
26236         if(this.overlay){
26237             this.overlay.hide();
26238         }
26239         var newSize;
26240         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26241             newSize = this.dragSpecs.startSize + 
26242                 (this.placement == Roo.SplitBar.LEFT ?
26243                     endPoint[0] - this.dragSpecs.startPoint[0] :
26244                     this.dragSpecs.startPoint[0] - endPoint[0]
26245                 );
26246         }else{
26247             newSize = this.dragSpecs.startSize + 
26248                 (this.placement == Roo.SplitBar.TOP ?
26249                     endPoint[1] - this.dragSpecs.startPoint[1] :
26250                     this.dragSpecs.startPoint[1] - endPoint[1]
26251                 );
26252         }
26253         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26254         if(newSize != this.dragSpecs.startSize){
26255             if(this.fireEvent('beforeapply', this, newSize) !== false){
26256                 this.adapter.setElementSize(this, newSize);
26257                 this.fireEvent("moved", this, newSize);
26258                 this.fireEvent("resize", this, newSize);
26259             }
26260         }
26261     },
26262     
26263     /**
26264      * Get the adapter this SplitBar uses
26265      * @return The adapter object
26266      */
26267     getAdapter : function(){
26268         return this.adapter;
26269     },
26270     
26271     /**
26272      * Set the adapter this SplitBar uses
26273      * @param {Object} adapter A SplitBar adapter object
26274      */
26275     setAdapter : function(adapter){
26276         this.adapter = adapter;
26277         this.adapter.init(this);
26278     },
26279     
26280     /**
26281      * Gets the minimum size for the resizing element
26282      * @return {Number} The minimum size
26283      */
26284     getMinimumSize : function(){
26285         return this.minSize;
26286     },
26287     
26288     /**
26289      * Sets the minimum size for the resizing element
26290      * @param {Number} minSize The minimum size
26291      */
26292     setMinimumSize : function(minSize){
26293         this.minSize = minSize;
26294     },
26295     
26296     /**
26297      * Gets the maximum size for the resizing element
26298      * @return {Number} The maximum size
26299      */
26300     getMaximumSize : function(){
26301         return this.maxSize;
26302     },
26303     
26304     /**
26305      * Sets the maximum size for the resizing element
26306      * @param {Number} maxSize The maximum size
26307      */
26308     setMaximumSize : function(maxSize){
26309         this.maxSize = maxSize;
26310     },
26311     
26312     /**
26313      * Sets the initialize size for the resizing element
26314      * @param {Number} size The initial size
26315      */
26316     setCurrentSize : function(size){
26317         var oldAnimate = this.animate;
26318         this.animate = false;
26319         this.adapter.setElementSize(this, size);
26320         this.animate = oldAnimate;
26321     },
26322     
26323     /**
26324      * Destroy this splitbar. 
26325      * @param {Boolean} removeEl True to remove the element
26326      */
26327     destroy : function(removeEl){
26328         if(this.shim){
26329             this.shim.remove();
26330         }
26331         this.dd.unreg();
26332         this.proxy.parentNode.removeChild(this.proxy);
26333         if(removeEl){
26334             this.el.remove();
26335         }
26336     }
26337 });
26338
26339 /**
26340  * @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.
26341  */
26342 Roo.SplitBar.createProxy = function(dir){
26343     var proxy = new Roo.Element(document.createElement("div"));
26344     proxy.unselectable();
26345     var cls = 'x-splitbar-proxy';
26346     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26347     document.body.appendChild(proxy.dom);
26348     return proxy.dom;
26349 };
26350
26351 /** 
26352  * @class Roo.SplitBar.BasicLayoutAdapter
26353  * Default Adapter. It assumes the splitter and resizing element are not positioned
26354  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26355  */
26356 Roo.SplitBar.BasicLayoutAdapter = function(){
26357 };
26358
26359 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26360     // do nothing for now
26361     init : function(s){
26362     
26363     },
26364     /**
26365      * Called before drag operations to get the current size of the resizing element. 
26366      * @param {Roo.SplitBar} s The SplitBar using this adapter
26367      */
26368      getElementSize : function(s){
26369         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26370             return s.resizingEl.getWidth();
26371         }else{
26372             return s.resizingEl.getHeight();
26373         }
26374     },
26375     
26376     /**
26377      * Called after drag operations to set the size of the resizing element.
26378      * @param {Roo.SplitBar} s The SplitBar using this adapter
26379      * @param {Number} newSize The new size to set
26380      * @param {Function} onComplete A function to be invoked when resizing is complete
26381      */
26382     setElementSize : function(s, newSize, onComplete){
26383         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26384             if(!s.animate){
26385                 s.resizingEl.setWidth(newSize);
26386                 if(onComplete){
26387                     onComplete(s, newSize);
26388                 }
26389             }else{
26390                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26391             }
26392         }else{
26393             
26394             if(!s.animate){
26395                 s.resizingEl.setHeight(newSize);
26396                 if(onComplete){
26397                     onComplete(s, newSize);
26398                 }
26399             }else{
26400                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26401             }
26402         }
26403     }
26404 };
26405
26406 /** 
26407  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26408  * @extends Roo.SplitBar.BasicLayoutAdapter
26409  * Adapter that  moves the splitter element to align with the resized sizing element. 
26410  * Used with an absolute positioned SplitBar.
26411  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26412  * document.body, make sure you assign an id to the body element.
26413  */
26414 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26415     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26416     this.container = Roo.get(container);
26417 };
26418
26419 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26420     init : function(s){
26421         this.basic.init(s);
26422     },
26423     
26424     getElementSize : function(s){
26425         return this.basic.getElementSize(s);
26426     },
26427     
26428     setElementSize : function(s, newSize, onComplete){
26429         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26430     },
26431     
26432     moveSplitter : function(s){
26433         var yes = Roo.SplitBar;
26434         switch(s.placement){
26435             case yes.LEFT:
26436                 s.el.setX(s.resizingEl.getRight());
26437                 break;
26438             case yes.RIGHT:
26439                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26440                 break;
26441             case yes.TOP:
26442                 s.el.setY(s.resizingEl.getBottom());
26443                 break;
26444             case yes.BOTTOM:
26445                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26446                 break;
26447         }
26448     }
26449 };
26450
26451 /**
26452  * Orientation constant - Create a vertical SplitBar
26453  * @static
26454  * @type Number
26455  */
26456 Roo.SplitBar.VERTICAL = 1;
26457
26458 /**
26459  * Orientation constant - Create a horizontal SplitBar
26460  * @static
26461  * @type Number
26462  */
26463 Roo.SplitBar.HORIZONTAL = 2;
26464
26465 /**
26466  * Placement constant - The resizing element is to the left of the splitter element
26467  * @static
26468  * @type Number
26469  */
26470 Roo.SplitBar.LEFT = 1;
26471
26472 /**
26473  * Placement constant - The resizing element is to the right of the splitter element
26474  * @static
26475  * @type Number
26476  */
26477 Roo.SplitBar.RIGHT = 2;
26478
26479 /**
26480  * Placement constant - The resizing element is positioned above the splitter element
26481  * @static
26482  * @type Number
26483  */
26484 Roo.SplitBar.TOP = 3;
26485
26486 /**
26487  * Placement constant - The resizing element is positioned under splitter element
26488  * @static
26489  * @type Number
26490  */
26491 Roo.SplitBar.BOTTOM = 4;
26492 /*
26493  * Based on:
26494  * Ext JS Library 1.1.1
26495  * Copyright(c) 2006-2007, Ext JS, LLC.
26496  *
26497  * Originally Released Under LGPL - original licence link has changed is not relivant.
26498  *
26499  * Fork - LGPL
26500  * <script type="text/javascript">
26501  */
26502
26503 /**
26504  * @class Roo.View
26505  * @extends Roo.util.Observable
26506  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26507  * This class also supports single and multi selection modes. <br>
26508  * Create a data model bound view:
26509  <pre><code>
26510  var store = new Roo.data.Store(...);
26511
26512  var view = new Roo.View({
26513     el : "my-element",
26514     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26515  
26516     singleSelect: true,
26517     selectedClass: "ydataview-selected",
26518     store: store
26519  });
26520
26521  // listen for node click?
26522  view.on("click", function(vw, index, node, e){
26523  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26524  });
26525
26526  // load XML data
26527  dataModel.load("foobar.xml");
26528  </code></pre>
26529  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26530  * <br><br>
26531  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26532  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26533  * 
26534  * Note: old style constructor is still suported (container, template, config)
26535  * 
26536  * @constructor
26537  * Create a new View
26538  * @param {Object} config The config object
26539  * 
26540  */
26541 Roo.View = function(config, depreciated_tpl, depreciated_config){
26542     
26543     this.parent = false;
26544     
26545     if (typeof(depreciated_tpl) == 'undefined') {
26546         // new way.. - universal constructor.
26547         Roo.apply(this, config);
26548         this.el  = Roo.get(this.el);
26549     } else {
26550         // old format..
26551         this.el  = Roo.get(config);
26552         this.tpl = depreciated_tpl;
26553         Roo.apply(this, depreciated_config);
26554     }
26555     this.wrapEl  = this.el.wrap().wrap();
26556     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26557     
26558     
26559     if(typeof(this.tpl) == "string"){
26560         this.tpl = new Roo.Template(this.tpl);
26561     } else {
26562         // support xtype ctors..
26563         this.tpl = new Roo.factory(this.tpl, Roo);
26564     }
26565     
26566     
26567     this.tpl.compile();
26568     
26569     /** @private */
26570     this.addEvents({
26571         /**
26572          * @event beforeclick
26573          * Fires before a click is processed. Returns false to cancel the default action.
26574          * @param {Roo.View} this
26575          * @param {Number} index The index of the target node
26576          * @param {HTMLElement} node The target node
26577          * @param {Roo.EventObject} e The raw event object
26578          */
26579             "beforeclick" : true,
26580         /**
26581          * @event click
26582          * Fires when a template node is clicked.
26583          * @param {Roo.View} this
26584          * @param {Number} index The index of the target node
26585          * @param {HTMLElement} node The target node
26586          * @param {Roo.EventObject} e The raw event object
26587          */
26588             "click" : true,
26589         /**
26590          * @event dblclick
26591          * Fires when a template node is double clicked.
26592          * @param {Roo.View} this
26593          * @param {Number} index The index of the target node
26594          * @param {HTMLElement} node The target node
26595          * @param {Roo.EventObject} e The raw event object
26596          */
26597             "dblclick" : true,
26598         /**
26599          * @event contextmenu
26600          * Fires when a template node is right clicked.
26601          * @param {Roo.View} this
26602          * @param {Number} index The index of the target node
26603          * @param {HTMLElement} node The target node
26604          * @param {Roo.EventObject} e The raw event object
26605          */
26606             "contextmenu" : true,
26607         /**
26608          * @event selectionchange
26609          * Fires when the selected nodes change.
26610          * @param {Roo.View} this
26611          * @param {Array} selections Array of the selected nodes
26612          */
26613             "selectionchange" : true,
26614     
26615         /**
26616          * @event beforeselect
26617          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26618          * @param {Roo.View} this
26619          * @param {HTMLElement} node The node to be selected
26620          * @param {Array} selections Array of currently selected nodes
26621          */
26622             "beforeselect" : true,
26623         /**
26624          * @event preparedata
26625          * Fires on every row to render, to allow you to change the data.
26626          * @param {Roo.View} this
26627          * @param {Object} data to be rendered (change this)
26628          */
26629           "preparedata" : true
26630           
26631           
26632         });
26633
26634
26635
26636     this.el.on({
26637         "click": this.onClick,
26638         "dblclick": this.onDblClick,
26639         "contextmenu": this.onContextMenu,
26640         scope:this
26641     });
26642
26643     this.selections = [];
26644     this.nodes = [];
26645     this.cmp = new Roo.CompositeElementLite([]);
26646     if(this.store){
26647         this.store = Roo.factory(this.store, Roo.data);
26648         this.setStore(this.store, true);
26649     }
26650     
26651     if ( this.footer && this.footer.xtype) {
26652            
26653          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26654         
26655         this.footer.dataSource = this.store;
26656         this.footer.container = fctr;
26657         this.footer = Roo.factory(this.footer, Roo);
26658         fctr.insertFirst(this.el);
26659         
26660         // this is a bit insane - as the paging toolbar seems to detach the el..
26661 //        dom.parentNode.parentNode.parentNode
26662          // they get detached?
26663     }
26664     
26665     
26666     Roo.View.superclass.constructor.call(this);
26667     
26668     
26669 };
26670
26671 Roo.extend(Roo.View, Roo.util.Observable, {
26672     
26673      /**
26674      * @cfg {Roo.data.Store} store Data store to load data from.
26675      */
26676     store : false,
26677     
26678     /**
26679      * @cfg {String|Roo.Element} el The container element.
26680      */
26681     el : '',
26682     
26683     /**
26684      * @cfg {String|Roo.Template} tpl The template used by this View 
26685      */
26686     tpl : false,
26687     /**
26688      * @cfg {String} dataName the named area of the template to use as the data area
26689      *                          Works with domtemplates roo-name="name"
26690      */
26691     dataName: false,
26692     /**
26693      * @cfg {String} selectedClass The css class to add to selected nodes
26694      */
26695     selectedClass : "x-view-selected",
26696      /**
26697      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26698      */
26699     emptyText : "",
26700     
26701     /**
26702      * @cfg {String} text to display on mask (default Loading)
26703      */
26704     mask : false,
26705     /**
26706      * @cfg {Boolean} multiSelect Allow multiple selection
26707      */
26708     multiSelect : false,
26709     /**
26710      * @cfg {Boolean} singleSelect Allow single selection
26711      */
26712     singleSelect:  false,
26713     
26714     /**
26715      * @cfg {Boolean} toggleSelect - selecting 
26716      */
26717     toggleSelect : false,
26718     
26719     /**
26720      * @cfg {Boolean} tickable - selecting 
26721      */
26722     tickable : false,
26723     
26724     /**
26725      * Returns the element this view is bound to.
26726      * @return {Roo.Element}
26727      */
26728     getEl : function(){
26729         return this.wrapEl;
26730     },
26731     
26732     
26733
26734     /**
26735      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26736      */
26737     refresh : function(){
26738         //Roo.log('refresh');
26739         var t = this.tpl;
26740         
26741         // if we are using something like 'domtemplate', then
26742         // the what gets used is:
26743         // t.applySubtemplate(NAME, data, wrapping data..)
26744         // the outer template then get' applied with
26745         //     the store 'extra data'
26746         // and the body get's added to the
26747         //      roo-name="data" node?
26748         //      <span class='roo-tpl-{name}'></span> ?????
26749         
26750         
26751         
26752         this.clearSelections();
26753         this.el.update("");
26754         var html = [];
26755         var records = this.store.getRange();
26756         if(records.length < 1) {
26757             
26758             // is this valid??  = should it render a template??
26759             
26760             this.el.update(this.emptyText);
26761             return;
26762         }
26763         var el = this.el;
26764         if (this.dataName) {
26765             this.el.update(t.apply(this.store.meta)); //????
26766             el = this.el.child('.roo-tpl-' + this.dataName);
26767         }
26768         
26769         for(var i = 0, len = records.length; i < len; i++){
26770             var data = this.prepareData(records[i].data, i, records[i]);
26771             this.fireEvent("preparedata", this, data, i, records[i]);
26772             
26773             var d = Roo.apply({}, data);
26774             
26775             if(this.tickable){
26776                 Roo.apply(d, {'roo-id' : Roo.id()});
26777                 
26778                 var _this = this;
26779             
26780                 Roo.each(this.parent.item, function(item){
26781                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26782                         return;
26783                     }
26784                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26785                 });
26786             }
26787             
26788             html[html.length] = Roo.util.Format.trim(
26789                 this.dataName ?
26790                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26791                     t.apply(d)
26792             );
26793         }
26794         
26795         
26796         
26797         el.update(html.join(""));
26798         this.nodes = el.dom.childNodes;
26799         this.updateIndexes(0);
26800     },
26801     
26802
26803     /**
26804      * Function to override to reformat the data that is sent to
26805      * the template for each node.
26806      * DEPRICATED - use the preparedata event handler.
26807      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26808      * a JSON object for an UpdateManager bound view).
26809      */
26810     prepareData : function(data, index, record)
26811     {
26812         this.fireEvent("preparedata", this, data, index, record);
26813         return data;
26814     },
26815
26816     onUpdate : function(ds, record){
26817         // Roo.log('on update');   
26818         this.clearSelections();
26819         var index = this.store.indexOf(record);
26820         var n = this.nodes[index];
26821         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26822         n.parentNode.removeChild(n);
26823         this.updateIndexes(index, index);
26824     },
26825
26826     
26827     
26828 // --------- FIXME     
26829     onAdd : function(ds, records, index)
26830     {
26831         //Roo.log(['on Add', ds, records, index] );        
26832         this.clearSelections();
26833         if(this.nodes.length == 0){
26834             this.refresh();
26835             return;
26836         }
26837         var n = this.nodes[index];
26838         for(var i = 0, len = records.length; i < len; i++){
26839             var d = this.prepareData(records[i].data, i, records[i]);
26840             if(n){
26841                 this.tpl.insertBefore(n, d);
26842             }else{
26843                 
26844                 this.tpl.append(this.el, d);
26845             }
26846         }
26847         this.updateIndexes(index);
26848     },
26849
26850     onRemove : function(ds, record, index){
26851        // Roo.log('onRemove');
26852         this.clearSelections();
26853         var el = this.dataName  ?
26854             this.el.child('.roo-tpl-' + this.dataName) :
26855             this.el; 
26856         
26857         el.dom.removeChild(this.nodes[index]);
26858         this.updateIndexes(index);
26859     },
26860
26861     /**
26862      * Refresh an individual node.
26863      * @param {Number} index
26864      */
26865     refreshNode : function(index){
26866         this.onUpdate(this.store, this.store.getAt(index));
26867     },
26868
26869     updateIndexes : function(startIndex, endIndex){
26870         var ns = this.nodes;
26871         startIndex = startIndex || 0;
26872         endIndex = endIndex || ns.length - 1;
26873         for(var i = startIndex; i <= endIndex; i++){
26874             ns[i].nodeIndex = i;
26875         }
26876     },
26877
26878     /**
26879      * Changes the data store this view uses and refresh the view.
26880      * @param {Store} store
26881      */
26882     setStore : function(store, initial){
26883         if(!initial && this.store){
26884             this.store.un("datachanged", this.refresh);
26885             this.store.un("add", this.onAdd);
26886             this.store.un("remove", this.onRemove);
26887             this.store.un("update", this.onUpdate);
26888             this.store.un("clear", this.refresh);
26889             this.store.un("beforeload", this.onBeforeLoad);
26890             this.store.un("load", this.onLoad);
26891             this.store.un("loadexception", this.onLoad);
26892         }
26893         if(store){
26894           
26895             store.on("datachanged", this.refresh, this);
26896             store.on("add", this.onAdd, this);
26897             store.on("remove", this.onRemove, this);
26898             store.on("update", this.onUpdate, this);
26899             store.on("clear", this.refresh, this);
26900             store.on("beforeload", this.onBeforeLoad, this);
26901             store.on("load", this.onLoad, this);
26902             store.on("loadexception", this.onLoad, this);
26903         }
26904         
26905         if(store){
26906             this.refresh();
26907         }
26908     },
26909     /**
26910      * onbeforeLoad - masks the loading area.
26911      *
26912      */
26913     onBeforeLoad : function(store,opts)
26914     {
26915          //Roo.log('onBeforeLoad');   
26916         if (!opts.add) {
26917             this.el.update("");
26918         }
26919         this.el.mask(this.mask ? this.mask : "Loading" ); 
26920     },
26921     onLoad : function ()
26922     {
26923         this.el.unmask();
26924     },
26925     
26926
26927     /**
26928      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26929      * @param {HTMLElement} node
26930      * @return {HTMLElement} The template node
26931      */
26932     findItemFromChild : function(node){
26933         var el = this.dataName  ?
26934             this.el.child('.roo-tpl-' + this.dataName,true) :
26935             this.el.dom; 
26936         
26937         if(!node || node.parentNode == el){
26938                     return node;
26939             }
26940             var p = node.parentNode;
26941             while(p && p != el){
26942             if(p.parentNode == el){
26943                 return p;
26944             }
26945             p = p.parentNode;
26946         }
26947             return null;
26948     },
26949
26950     /** @ignore */
26951     onClick : function(e){
26952         var item = this.findItemFromChild(e.getTarget());
26953         if(item){
26954             var index = this.indexOf(item);
26955             if(this.onItemClick(item, index, e) !== false){
26956                 this.fireEvent("click", this, index, item, e);
26957             }
26958         }else{
26959             this.clearSelections();
26960         }
26961     },
26962
26963     /** @ignore */
26964     onContextMenu : function(e){
26965         var item = this.findItemFromChild(e.getTarget());
26966         if(item){
26967             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26968         }
26969     },
26970
26971     /** @ignore */
26972     onDblClick : function(e){
26973         var item = this.findItemFromChild(e.getTarget());
26974         if(item){
26975             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26976         }
26977     },
26978
26979     onItemClick : function(item, index, e)
26980     {
26981         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26982             return false;
26983         }
26984         if (this.toggleSelect) {
26985             var m = this.isSelected(item) ? 'unselect' : 'select';
26986             //Roo.log(m);
26987             var _t = this;
26988             _t[m](item, true, false);
26989             return true;
26990         }
26991         if(this.multiSelect || this.singleSelect){
26992             if(this.multiSelect && e.shiftKey && this.lastSelection){
26993                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26994             }else{
26995                 this.select(item, this.multiSelect && e.ctrlKey);
26996                 this.lastSelection = item;
26997             }
26998             
26999             if(!this.tickable){
27000                 e.preventDefault();
27001             }
27002             
27003         }
27004         return true;
27005     },
27006
27007     /**
27008      * Get the number of selected nodes.
27009      * @return {Number}
27010      */
27011     getSelectionCount : function(){
27012         return this.selections.length;
27013     },
27014
27015     /**
27016      * Get the currently selected nodes.
27017      * @return {Array} An array of HTMLElements
27018      */
27019     getSelectedNodes : function(){
27020         return this.selections;
27021     },
27022
27023     /**
27024      * Get the indexes of the selected nodes.
27025      * @return {Array}
27026      */
27027     getSelectedIndexes : function(){
27028         var indexes = [], s = this.selections;
27029         for(var i = 0, len = s.length; i < len; i++){
27030             indexes.push(s[i].nodeIndex);
27031         }
27032         return indexes;
27033     },
27034
27035     /**
27036      * Clear all selections
27037      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27038      */
27039     clearSelections : function(suppressEvent){
27040         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27041             this.cmp.elements = this.selections;
27042             this.cmp.removeClass(this.selectedClass);
27043             this.selections = [];
27044             if(!suppressEvent){
27045                 this.fireEvent("selectionchange", this, this.selections);
27046             }
27047         }
27048     },
27049
27050     /**
27051      * Returns true if the passed node is selected
27052      * @param {HTMLElement/Number} node The node or node index
27053      * @return {Boolean}
27054      */
27055     isSelected : function(node){
27056         var s = this.selections;
27057         if(s.length < 1){
27058             return false;
27059         }
27060         node = this.getNode(node);
27061         return s.indexOf(node) !== -1;
27062     },
27063
27064     /**
27065      * Selects nodes.
27066      * @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
27067      * @param {Boolean} keepExisting (optional) true to keep existing selections
27068      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27069      */
27070     select : function(nodeInfo, keepExisting, suppressEvent){
27071         if(nodeInfo instanceof Array){
27072             if(!keepExisting){
27073                 this.clearSelections(true);
27074             }
27075             for(var i = 0, len = nodeInfo.length; i < len; i++){
27076                 this.select(nodeInfo[i], true, true);
27077             }
27078             return;
27079         } 
27080         var node = this.getNode(nodeInfo);
27081         if(!node || this.isSelected(node)){
27082             return; // already selected.
27083         }
27084         if(!keepExisting){
27085             this.clearSelections(true);
27086         }
27087         
27088         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27089             Roo.fly(node).addClass(this.selectedClass);
27090             this.selections.push(node);
27091             if(!suppressEvent){
27092                 this.fireEvent("selectionchange", this, this.selections);
27093             }
27094         }
27095         
27096         
27097     },
27098       /**
27099      * Unselects nodes.
27100      * @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
27101      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27103      */
27104     unselect : function(nodeInfo, keepExisting, suppressEvent)
27105     {
27106         if(nodeInfo instanceof Array){
27107             Roo.each(this.selections, function(s) {
27108                 this.unselect(s, nodeInfo);
27109             }, this);
27110             return;
27111         }
27112         var node = this.getNode(nodeInfo);
27113         if(!node || !this.isSelected(node)){
27114             //Roo.log("not selected");
27115             return; // not selected.
27116         }
27117         // fireevent???
27118         var ns = [];
27119         Roo.each(this.selections, function(s) {
27120             if (s == node ) {
27121                 Roo.fly(node).removeClass(this.selectedClass);
27122
27123                 return;
27124             }
27125             ns.push(s);
27126         },this);
27127         
27128         this.selections= ns;
27129         this.fireEvent("selectionchange", this, this.selections);
27130     },
27131
27132     /**
27133      * Gets a template node.
27134      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27135      * @return {HTMLElement} The node or null if it wasn't found
27136      */
27137     getNode : function(nodeInfo){
27138         if(typeof nodeInfo == "string"){
27139             return document.getElementById(nodeInfo);
27140         }else if(typeof nodeInfo == "number"){
27141             return this.nodes[nodeInfo];
27142         }
27143         return nodeInfo;
27144     },
27145
27146     /**
27147      * Gets a range template nodes.
27148      * @param {Number} startIndex
27149      * @param {Number} endIndex
27150      * @return {Array} An array of nodes
27151      */
27152     getNodes : function(start, end){
27153         var ns = this.nodes;
27154         start = start || 0;
27155         end = typeof end == "undefined" ? ns.length - 1 : end;
27156         var nodes = [];
27157         if(start <= end){
27158             for(var i = start; i <= end; i++){
27159                 nodes.push(ns[i]);
27160             }
27161         } else{
27162             for(var i = start; i >= end; i--){
27163                 nodes.push(ns[i]);
27164             }
27165         }
27166         return nodes;
27167     },
27168
27169     /**
27170      * Finds the index of the passed node
27171      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27172      * @return {Number} The index of the node or -1
27173      */
27174     indexOf : function(node){
27175         node = this.getNode(node);
27176         if(typeof node.nodeIndex == "number"){
27177             return node.nodeIndex;
27178         }
27179         var ns = this.nodes;
27180         for(var i = 0, len = ns.length; i < len; i++){
27181             if(ns[i] == node){
27182                 return i;
27183             }
27184         }
27185         return -1;
27186     }
27187 });
27188 /*
27189  * Based on:
27190  * Ext JS Library 1.1.1
27191  * Copyright(c) 2006-2007, Ext JS, LLC.
27192  *
27193  * Originally Released Under LGPL - original licence link has changed is not relivant.
27194  *
27195  * Fork - LGPL
27196  * <script type="text/javascript">
27197  */
27198
27199 /**
27200  * @class Roo.JsonView
27201  * @extends Roo.View
27202  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27203 <pre><code>
27204 var view = new Roo.JsonView({
27205     container: "my-element",
27206     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27207     multiSelect: true, 
27208     jsonRoot: "data" 
27209 });
27210
27211 // listen for node click?
27212 view.on("click", function(vw, index, node, e){
27213     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27214 });
27215
27216 // direct load of JSON data
27217 view.load("foobar.php");
27218
27219 // Example from my blog list
27220 var tpl = new Roo.Template(
27221     '&lt;div class="entry"&gt;' +
27222     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27223     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27224     "&lt;/div&gt;&lt;hr /&gt;"
27225 );
27226
27227 var moreView = new Roo.JsonView({
27228     container :  "entry-list", 
27229     template : tpl,
27230     jsonRoot: "posts"
27231 });
27232 moreView.on("beforerender", this.sortEntries, this);
27233 moreView.load({
27234     url: "/blog/get-posts.php",
27235     params: "allposts=true",
27236     text: "Loading Blog Entries..."
27237 });
27238 </code></pre>
27239
27240 * Note: old code is supported with arguments : (container, template, config)
27241
27242
27243  * @constructor
27244  * Create a new JsonView
27245  * 
27246  * @param {Object} config The config object
27247  * 
27248  */
27249 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27250     
27251     
27252     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27253
27254     var um = this.el.getUpdateManager();
27255     um.setRenderer(this);
27256     um.on("update", this.onLoad, this);
27257     um.on("failure", this.onLoadException, this);
27258
27259     /**
27260      * @event beforerender
27261      * Fires before rendering of the downloaded JSON data.
27262      * @param {Roo.JsonView} this
27263      * @param {Object} data The JSON data loaded
27264      */
27265     /**
27266      * @event load
27267      * Fires when data is loaded.
27268      * @param {Roo.JsonView} this
27269      * @param {Object} data The JSON data loaded
27270      * @param {Object} response The raw Connect response object
27271      */
27272     /**
27273      * @event loadexception
27274      * Fires when loading fails.
27275      * @param {Roo.JsonView} this
27276      * @param {Object} response The raw Connect response object
27277      */
27278     this.addEvents({
27279         'beforerender' : true,
27280         'load' : true,
27281         'loadexception' : true
27282     });
27283 };
27284 Roo.extend(Roo.JsonView, Roo.View, {
27285     /**
27286      * @type {String} The root property in the loaded JSON object that contains the data
27287      */
27288     jsonRoot : "",
27289
27290     /**
27291      * Refreshes the view.
27292      */
27293     refresh : function(){
27294         this.clearSelections();
27295         this.el.update("");
27296         var html = [];
27297         var o = this.jsonData;
27298         if(o && o.length > 0){
27299             for(var i = 0, len = o.length; i < len; i++){
27300                 var data = this.prepareData(o[i], i, o);
27301                 html[html.length] = this.tpl.apply(data);
27302             }
27303         }else{
27304             html.push(this.emptyText);
27305         }
27306         this.el.update(html.join(""));
27307         this.nodes = this.el.dom.childNodes;
27308         this.updateIndexes(0);
27309     },
27310
27311     /**
27312      * 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.
27313      * @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:
27314      <pre><code>
27315      view.load({
27316          url: "your-url.php",
27317          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27318          callback: yourFunction,
27319          scope: yourObject, //(optional scope)
27320          discardUrl: false,
27321          nocache: false,
27322          text: "Loading...",
27323          timeout: 30,
27324          scripts: false
27325      });
27326      </code></pre>
27327      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27328      * 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.
27329      * @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}
27330      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27331      * @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.
27332      */
27333     load : function(){
27334         var um = this.el.getUpdateManager();
27335         um.update.apply(um, arguments);
27336     },
27337
27338     // note - render is a standard framework call...
27339     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27340     render : function(el, response){
27341         
27342         this.clearSelections();
27343         this.el.update("");
27344         var o;
27345         try{
27346             if (response != '') {
27347                 o = Roo.util.JSON.decode(response.responseText);
27348                 if(this.jsonRoot){
27349                     
27350                     o = o[this.jsonRoot];
27351                 }
27352             }
27353         } catch(e){
27354         }
27355         /**
27356          * The current JSON data or null
27357          */
27358         this.jsonData = o;
27359         this.beforeRender();
27360         this.refresh();
27361     },
27362
27363 /**
27364  * Get the number of records in the current JSON dataset
27365  * @return {Number}
27366  */
27367     getCount : function(){
27368         return this.jsonData ? this.jsonData.length : 0;
27369     },
27370
27371 /**
27372  * Returns the JSON object for the specified node(s)
27373  * @param {HTMLElement/Array} node The node or an array of nodes
27374  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27375  * you get the JSON object for the node
27376  */
27377     getNodeData : function(node){
27378         if(node instanceof Array){
27379             var data = [];
27380             for(var i = 0, len = node.length; i < len; i++){
27381                 data.push(this.getNodeData(node[i]));
27382             }
27383             return data;
27384         }
27385         return this.jsonData[this.indexOf(node)] || null;
27386     },
27387
27388     beforeRender : function(){
27389         this.snapshot = this.jsonData;
27390         if(this.sortInfo){
27391             this.sort.apply(this, this.sortInfo);
27392         }
27393         this.fireEvent("beforerender", this, this.jsonData);
27394     },
27395
27396     onLoad : function(el, o){
27397         this.fireEvent("load", this, this.jsonData, o);
27398     },
27399
27400     onLoadException : function(el, o){
27401         this.fireEvent("loadexception", this, o);
27402     },
27403
27404 /**
27405  * Filter the data by a specific property.
27406  * @param {String} property A property on your JSON objects
27407  * @param {String/RegExp} value Either string that the property values
27408  * should start with, or a RegExp to test against the property
27409  */
27410     filter : function(property, value){
27411         if(this.jsonData){
27412             var data = [];
27413             var ss = this.snapshot;
27414             if(typeof value == "string"){
27415                 var vlen = value.length;
27416                 if(vlen == 0){
27417                     this.clearFilter();
27418                     return;
27419                 }
27420                 value = value.toLowerCase();
27421                 for(var i = 0, len = ss.length; i < len; i++){
27422                     var o = ss[i];
27423                     if(o[property].substr(0, vlen).toLowerCase() == value){
27424                         data.push(o);
27425                     }
27426                 }
27427             } else if(value.exec){ // regex?
27428                 for(var i = 0, len = ss.length; i < len; i++){
27429                     var o = ss[i];
27430                     if(value.test(o[property])){
27431                         data.push(o);
27432                     }
27433                 }
27434             } else{
27435                 return;
27436             }
27437             this.jsonData = data;
27438             this.refresh();
27439         }
27440     },
27441
27442 /**
27443  * Filter by a function. The passed function will be called with each
27444  * object in the current dataset. If the function returns true the value is kept,
27445  * otherwise it is filtered.
27446  * @param {Function} fn
27447  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27448  */
27449     filterBy : function(fn, scope){
27450         if(this.jsonData){
27451             var data = [];
27452             var ss = this.snapshot;
27453             for(var i = 0, len = ss.length; i < len; i++){
27454                 var o = ss[i];
27455                 if(fn.call(scope || this, o)){
27456                     data.push(o);
27457                 }
27458             }
27459             this.jsonData = data;
27460             this.refresh();
27461         }
27462     },
27463
27464 /**
27465  * Clears the current filter.
27466  */
27467     clearFilter : function(){
27468         if(this.snapshot && this.jsonData != this.snapshot){
27469             this.jsonData = this.snapshot;
27470             this.refresh();
27471         }
27472     },
27473
27474
27475 /**
27476  * Sorts the data for this view and refreshes it.
27477  * @param {String} property A property on your JSON objects to sort on
27478  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27479  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27480  */
27481     sort : function(property, dir, sortType){
27482         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27483         if(this.jsonData){
27484             var p = property;
27485             var dsc = dir && dir.toLowerCase() == "desc";
27486             var f = function(o1, o2){
27487                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27488                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27489                 ;
27490                 if(v1 < v2){
27491                     return dsc ? +1 : -1;
27492                 } else if(v1 > v2){
27493                     return dsc ? -1 : +1;
27494                 } else{
27495                     return 0;
27496                 }
27497             };
27498             this.jsonData.sort(f);
27499             this.refresh();
27500             if(this.jsonData != this.snapshot){
27501                 this.snapshot.sort(f);
27502             }
27503         }
27504     }
27505 });/*
27506  * Based on:
27507  * Ext JS Library 1.1.1
27508  * Copyright(c) 2006-2007, Ext JS, LLC.
27509  *
27510  * Originally Released Under LGPL - original licence link has changed is not relivant.
27511  *
27512  * Fork - LGPL
27513  * <script type="text/javascript">
27514  */
27515  
27516
27517 /**
27518  * @class Roo.ColorPalette
27519  * @extends Roo.Component
27520  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27521  * Here's an example of typical usage:
27522  * <pre><code>
27523 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27524 cp.render('my-div');
27525
27526 cp.on('select', function(palette, selColor){
27527     // do something with selColor
27528 });
27529 </code></pre>
27530  * @constructor
27531  * Create a new ColorPalette
27532  * @param {Object} config The config object
27533  */
27534 Roo.ColorPalette = function(config){
27535     Roo.ColorPalette.superclass.constructor.call(this, config);
27536     this.addEvents({
27537         /**
27538              * @event select
27539              * Fires when a color is selected
27540              * @param {ColorPalette} this
27541              * @param {String} color The 6-digit color hex code (without the # symbol)
27542              */
27543         select: true
27544     });
27545
27546     if(this.handler){
27547         this.on("select", this.handler, this.scope, true);
27548     }
27549 };
27550 Roo.extend(Roo.ColorPalette, Roo.Component, {
27551     /**
27552      * @cfg {String} itemCls
27553      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27554      */
27555     itemCls : "x-color-palette",
27556     /**
27557      * @cfg {String} value
27558      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27559      * the hex codes are case-sensitive.
27560      */
27561     value : null,
27562     clickEvent:'click',
27563     // private
27564     ctype: "Roo.ColorPalette",
27565
27566     /**
27567      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27568      */
27569     allowReselect : false,
27570
27571     /**
27572      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27573      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27574      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27575      * of colors with the width setting until the box is symmetrical.</p>
27576      * <p>You can override individual colors if needed:</p>
27577      * <pre><code>
27578 var cp = new Roo.ColorPalette();
27579 cp.colors[0] = "FF0000";  // change the first box to red
27580 </code></pre>
27581
27582 Or you can provide a custom array of your own for complete control:
27583 <pre><code>
27584 var cp = new Roo.ColorPalette();
27585 cp.colors = ["000000", "993300", "333300"];
27586 </code></pre>
27587      * @type Array
27588      */
27589     colors : [
27590         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27591         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27592         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27593         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27594         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27595     ],
27596
27597     // private
27598     onRender : function(container, position){
27599         var t = new Roo.MasterTemplate(
27600             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27601         );
27602         var c = this.colors;
27603         for(var i = 0, len = c.length; i < len; i++){
27604             t.add([c[i]]);
27605         }
27606         var el = document.createElement("div");
27607         el.className = this.itemCls;
27608         t.overwrite(el);
27609         container.dom.insertBefore(el, position);
27610         this.el = Roo.get(el);
27611         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27612         if(this.clickEvent != 'click'){
27613             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27614         }
27615     },
27616
27617     // private
27618     afterRender : function(){
27619         Roo.ColorPalette.superclass.afterRender.call(this);
27620         if(this.value){
27621             var s = this.value;
27622             this.value = null;
27623             this.select(s);
27624         }
27625     },
27626
27627     // private
27628     handleClick : function(e, t){
27629         e.preventDefault();
27630         if(!this.disabled){
27631             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27632             this.select(c.toUpperCase());
27633         }
27634     },
27635
27636     /**
27637      * Selects the specified color in the palette (fires the select event)
27638      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27639      */
27640     select : function(color){
27641         color = color.replace("#", "");
27642         if(color != this.value || this.allowReselect){
27643             var el = this.el;
27644             if(this.value){
27645                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27646             }
27647             el.child("a.color-"+color).addClass("x-color-palette-sel");
27648             this.value = color;
27649             this.fireEvent("select", this, color);
27650         }
27651     }
27652 });/*
27653  * Based on:
27654  * Ext JS Library 1.1.1
27655  * Copyright(c) 2006-2007, Ext JS, LLC.
27656  *
27657  * Originally Released Under LGPL - original licence link has changed is not relivant.
27658  *
27659  * Fork - LGPL
27660  * <script type="text/javascript">
27661  */
27662  
27663 /**
27664  * @class Roo.DatePicker
27665  * @extends Roo.Component
27666  * Simple date picker class.
27667  * @constructor
27668  * Create a new DatePicker
27669  * @param {Object} config The config object
27670  */
27671 Roo.DatePicker = function(config){
27672     Roo.DatePicker.superclass.constructor.call(this, config);
27673
27674     this.value = config && config.value ?
27675                  config.value.clearTime() : new Date().clearTime();
27676
27677     this.addEvents({
27678         /**
27679              * @event select
27680              * Fires when a date is selected
27681              * @param {DatePicker} this
27682              * @param {Date} date The selected date
27683              */
27684         'select': true,
27685         /**
27686              * @event monthchange
27687              * Fires when the displayed month changes 
27688              * @param {DatePicker} this
27689              * @param {Date} date The selected month
27690              */
27691         'monthchange': true
27692     });
27693
27694     if(this.handler){
27695         this.on("select", this.handler,  this.scope || this);
27696     }
27697     // build the disabledDatesRE
27698     if(!this.disabledDatesRE && this.disabledDates){
27699         var dd = this.disabledDates;
27700         var re = "(?:";
27701         for(var i = 0; i < dd.length; i++){
27702             re += dd[i];
27703             if(i != dd.length-1) {
27704                 re += "|";
27705             }
27706         }
27707         this.disabledDatesRE = new RegExp(re + ")");
27708     }
27709 };
27710
27711 Roo.extend(Roo.DatePicker, Roo.Component, {
27712     /**
27713      * @cfg {String} todayText
27714      * The text to display on the button that selects the current date (defaults to "Today")
27715      */
27716     todayText : "Today",
27717     /**
27718      * @cfg {String} okText
27719      * The text to display on the ok button
27720      */
27721     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27722     /**
27723      * @cfg {String} cancelText
27724      * The text to display on the cancel button
27725      */
27726     cancelText : "Cancel",
27727     /**
27728      * @cfg {String} todayTip
27729      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27730      */
27731     todayTip : "{0} (Spacebar)",
27732     /**
27733      * @cfg {Date} minDate
27734      * Minimum allowable date (JavaScript date object, defaults to null)
27735      */
27736     minDate : null,
27737     /**
27738      * @cfg {Date} maxDate
27739      * Maximum allowable date (JavaScript date object, defaults to null)
27740      */
27741     maxDate : null,
27742     /**
27743      * @cfg {String} minText
27744      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27745      */
27746     minText : "This date is before the minimum date",
27747     /**
27748      * @cfg {String} maxText
27749      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27750      */
27751     maxText : "This date is after the maximum date",
27752     /**
27753      * @cfg {String} format
27754      * The default date format string which can be overriden for localization support.  The format must be
27755      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27756      */
27757     format : "m/d/y",
27758     /**
27759      * @cfg {Array} disabledDays
27760      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27761      */
27762     disabledDays : null,
27763     /**
27764      * @cfg {String} disabledDaysText
27765      * The tooltip to display when the date falls on a disabled day (defaults to "")
27766      */
27767     disabledDaysText : "",
27768     /**
27769      * @cfg {RegExp} disabledDatesRE
27770      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27771      */
27772     disabledDatesRE : null,
27773     /**
27774      * @cfg {String} disabledDatesText
27775      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27776      */
27777     disabledDatesText : "",
27778     /**
27779      * @cfg {Boolean} constrainToViewport
27780      * True to constrain the date picker to the viewport (defaults to true)
27781      */
27782     constrainToViewport : true,
27783     /**
27784      * @cfg {Array} monthNames
27785      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27786      */
27787     monthNames : Date.monthNames,
27788     /**
27789      * @cfg {Array} dayNames
27790      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27791      */
27792     dayNames : Date.dayNames,
27793     /**
27794      * @cfg {String} nextText
27795      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27796      */
27797     nextText: 'Next Month (Control+Right)',
27798     /**
27799      * @cfg {String} prevText
27800      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27801      */
27802     prevText: 'Previous Month (Control+Left)',
27803     /**
27804      * @cfg {String} monthYearText
27805      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27806      */
27807     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27808     /**
27809      * @cfg {Number} startDay
27810      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27811      */
27812     startDay : 0,
27813     /**
27814      * @cfg {Bool} showClear
27815      * Show a clear button (usefull for date form elements that can be blank.)
27816      */
27817     
27818     showClear: false,
27819     
27820     /**
27821      * Sets the value of the date field
27822      * @param {Date} value The date to set
27823      */
27824     setValue : function(value){
27825         var old = this.value;
27826         
27827         if (typeof(value) == 'string') {
27828          
27829             value = Date.parseDate(value, this.format);
27830         }
27831         if (!value) {
27832             value = new Date();
27833         }
27834         
27835         this.value = value.clearTime(true);
27836         if(this.el){
27837             this.update(this.value);
27838         }
27839     },
27840
27841     /**
27842      * Gets the current selected value of the date field
27843      * @return {Date} The selected date
27844      */
27845     getValue : function(){
27846         return this.value;
27847     },
27848
27849     // private
27850     focus : function(){
27851         if(this.el){
27852             this.update(this.activeDate);
27853         }
27854     },
27855
27856     // privateval
27857     onRender : function(container, position){
27858         
27859         var m = [
27860              '<table cellspacing="0">',
27861                 '<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>',
27862                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27863         var dn = this.dayNames;
27864         for(var i = 0; i < 7; i++){
27865             var d = this.startDay+i;
27866             if(d > 6){
27867                 d = d-7;
27868             }
27869             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27870         }
27871         m[m.length] = "</tr></thead><tbody><tr>";
27872         for(var i = 0; i < 42; i++) {
27873             if(i % 7 == 0 && i != 0){
27874                 m[m.length] = "</tr><tr>";
27875             }
27876             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27877         }
27878         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27879             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27880
27881         var el = document.createElement("div");
27882         el.className = "x-date-picker";
27883         el.innerHTML = m.join("");
27884
27885         container.dom.insertBefore(el, position);
27886
27887         this.el = Roo.get(el);
27888         this.eventEl = Roo.get(el.firstChild);
27889
27890         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27891             handler: this.showPrevMonth,
27892             scope: this,
27893             preventDefault:true,
27894             stopDefault:true
27895         });
27896
27897         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27898             handler: this.showNextMonth,
27899             scope: this,
27900             preventDefault:true,
27901             stopDefault:true
27902         });
27903
27904         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27905
27906         this.monthPicker = this.el.down('div.x-date-mp');
27907         this.monthPicker.enableDisplayMode('block');
27908         
27909         var kn = new Roo.KeyNav(this.eventEl, {
27910             "left" : function(e){
27911                 e.ctrlKey ?
27912                     this.showPrevMonth() :
27913                     this.update(this.activeDate.add("d", -1));
27914             },
27915
27916             "right" : function(e){
27917                 e.ctrlKey ?
27918                     this.showNextMonth() :
27919                     this.update(this.activeDate.add("d", 1));
27920             },
27921
27922             "up" : function(e){
27923                 e.ctrlKey ?
27924                     this.showNextYear() :
27925                     this.update(this.activeDate.add("d", -7));
27926             },
27927
27928             "down" : function(e){
27929                 e.ctrlKey ?
27930                     this.showPrevYear() :
27931                     this.update(this.activeDate.add("d", 7));
27932             },
27933
27934             "pageUp" : function(e){
27935                 this.showNextMonth();
27936             },
27937
27938             "pageDown" : function(e){
27939                 this.showPrevMonth();
27940             },
27941
27942             "enter" : function(e){
27943                 e.stopPropagation();
27944                 return true;
27945             },
27946
27947             scope : this
27948         });
27949
27950         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27951
27952         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27953
27954         this.el.unselectable();
27955         
27956         this.cells = this.el.select("table.x-date-inner tbody td");
27957         this.textNodes = this.el.query("table.x-date-inner tbody span");
27958
27959         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27960             text: "&#160;",
27961             tooltip: this.monthYearText
27962         });
27963
27964         this.mbtn.on('click', this.showMonthPicker, this);
27965         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27966
27967
27968         var today = (new Date()).dateFormat(this.format);
27969         
27970         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27971         if (this.showClear) {
27972             baseTb.add( new Roo.Toolbar.Fill());
27973         }
27974         baseTb.add({
27975             text: String.format(this.todayText, today),
27976             tooltip: String.format(this.todayTip, today),
27977             handler: this.selectToday,
27978             scope: this
27979         });
27980         
27981         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27982             
27983         //});
27984         if (this.showClear) {
27985             
27986             baseTb.add( new Roo.Toolbar.Fill());
27987             baseTb.add({
27988                 text: '&#160;',
27989                 cls: 'x-btn-icon x-btn-clear',
27990                 handler: function() {
27991                     //this.value = '';
27992                     this.fireEvent("select", this, '');
27993                 },
27994                 scope: this
27995             });
27996         }
27997         
27998         
27999         if(Roo.isIE){
28000             this.el.repaint();
28001         }
28002         this.update(this.value);
28003     },
28004
28005     createMonthPicker : function(){
28006         if(!this.monthPicker.dom.firstChild){
28007             var buf = ['<table border="0" cellspacing="0">'];
28008             for(var i = 0; i < 6; i++){
28009                 buf.push(
28010                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28011                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28012                     i == 0 ?
28013                     '<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>' :
28014                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28015                 );
28016             }
28017             buf.push(
28018                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28019                     this.okText,
28020                     '</button><button type="button" class="x-date-mp-cancel">',
28021                     this.cancelText,
28022                     '</button></td></tr>',
28023                 '</table>'
28024             );
28025             this.monthPicker.update(buf.join(''));
28026             this.monthPicker.on('click', this.onMonthClick, this);
28027             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28028
28029             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28030             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28031
28032             this.mpMonths.each(function(m, a, i){
28033                 i += 1;
28034                 if((i%2) == 0){
28035                     m.dom.xmonth = 5 + Math.round(i * .5);
28036                 }else{
28037                     m.dom.xmonth = Math.round((i-1) * .5);
28038                 }
28039             });
28040         }
28041     },
28042
28043     showMonthPicker : function(){
28044         this.createMonthPicker();
28045         var size = this.el.getSize();
28046         this.monthPicker.setSize(size);
28047         this.monthPicker.child('table').setSize(size);
28048
28049         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28050         this.updateMPMonth(this.mpSelMonth);
28051         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28052         this.updateMPYear(this.mpSelYear);
28053
28054         this.monthPicker.slideIn('t', {duration:.2});
28055     },
28056
28057     updateMPYear : function(y){
28058         this.mpyear = y;
28059         var ys = this.mpYears.elements;
28060         for(var i = 1; i <= 10; i++){
28061             var td = ys[i-1], y2;
28062             if((i%2) == 0){
28063                 y2 = y + Math.round(i * .5);
28064                 td.firstChild.innerHTML = y2;
28065                 td.xyear = y2;
28066             }else{
28067                 y2 = y - (5-Math.round(i * .5));
28068                 td.firstChild.innerHTML = y2;
28069                 td.xyear = y2;
28070             }
28071             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28072         }
28073     },
28074
28075     updateMPMonth : function(sm){
28076         this.mpMonths.each(function(m, a, i){
28077             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28078         });
28079     },
28080
28081     selectMPMonth: function(m){
28082         
28083     },
28084
28085     onMonthClick : function(e, t){
28086         e.stopEvent();
28087         var el = new Roo.Element(t), pn;
28088         if(el.is('button.x-date-mp-cancel')){
28089             this.hideMonthPicker();
28090         }
28091         else if(el.is('button.x-date-mp-ok')){
28092             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28093             this.hideMonthPicker();
28094         }
28095         else if(pn = el.up('td.x-date-mp-month', 2)){
28096             this.mpMonths.removeClass('x-date-mp-sel');
28097             pn.addClass('x-date-mp-sel');
28098             this.mpSelMonth = pn.dom.xmonth;
28099         }
28100         else if(pn = el.up('td.x-date-mp-year', 2)){
28101             this.mpYears.removeClass('x-date-mp-sel');
28102             pn.addClass('x-date-mp-sel');
28103             this.mpSelYear = pn.dom.xyear;
28104         }
28105         else if(el.is('a.x-date-mp-prev')){
28106             this.updateMPYear(this.mpyear-10);
28107         }
28108         else if(el.is('a.x-date-mp-next')){
28109             this.updateMPYear(this.mpyear+10);
28110         }
28111     },
28112
28113     onMonthDblClick : function(e, t){
28114         e.stopEvent();
28115         var el = new Roo.Element(t), pn;
28116         if(pn = el.up('td.x-date-mp-month', 2)){
28117             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28118             this.hideMonthPicker();
28119         }
28120         else if(pn = el.up('td.x-date-mp-year', 2)){
28121             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28122             this.hideMonthPicker();
28123         }
28124     },
28125
28126     hideMonthPicker : function(disableAnim){
28127         if(this.monthPicker){
28128             if(disableAnim === true){
28129                 this.monthPicker.hide();
28130             }else{
28131                 this.monthPicker.slideOut('t', {duration:.2});
28132             }
28133         }
28134     },
28135
28136     // private
28137     showPrevMonth : function(e){
28138         this.update(this.activeDate.add("mo", -1));
28139     },
28140
28141     // private
28142     showNextMonth : function(e){
28143         this.update(this.activeDate.add("mo", 1));
28144     },
28145
28146     // private
28147     showPrevYear : function(){
28148         this.update(this.activeDate.add("y", -1));
28149     },
28150
28151     // private
28152     showNextYear : function(){
28153         this.update(this.activeDate.add("y", 1));
28154     },
28155
28156     // private
28157     handleMouseWheel : function(e){
28158         var delta = e.getWheelDelta();
28159         if(delta > 0){
28160             this.showPrevMonth();
28161             e.stopEvent();
28162         } else if(delta < 0){
28163             this.showNextMonth();
28164             e.stopEvent();
28165         }
28166     },
28167
28168     // private
28169     handleDateClick : function(e, t){
28170         e.stopEvent();
28171         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28172             this.setValue(new Date(t.dateValue));
28173             this.fireEvent("select", this, this.value);
28174         }
28175     },
28176
28177     // private
28178     selectToday : function(){
28179         this.setValue(new Date().clearTime());
28180         this.fireEvent("select", this, this.value);
28181     },
28182
28183     // private
28184     update : function(date)
28185     {
28186         var vd = this.activeDate;
28187         this.activeDate = date;
28188         if(vd && this.el){
28189             var t = date.getTime();
28190             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28191                 this.cells.removeClass("x-date-selected");
28192                 this.cells.each(function(c){
28193                    if(c.dom.firstChild.dateValue == t){
28194                        c.addClass("x-date-selected");
28195                        setTimeout(function(){
28196                             try{c.dom.firstChild.focus();}catch(e){}
28197                        }, 50);
28198                        return false;
28199                    }
28200                 });
28201                 return;
28202             }
28203         }
28204         
28205         var days = date.getDaysInMonth();
28206         var firstOfMonth = date.getFirstDateOfMonth();
28207         var startingPos = firstOfMonth.getDay()-this.startDay;
28208
28209         if(startingPos <= this.startDay){
28210             startingPos += 7;
28211         }
28212
28213         var pm = date.add("mo", -1);
28214         var prevStart = pm.getDaysInMonth()-startingPos;
28215
28216         var cells = this.cells.elements;
28217         var textEls = this.textNodes;
28218         days += startingPos;
28219
28220         // convert everything to numbers so it's fast
28221         var day = 86400000;
28222         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28223         var today = new Date().clearTime().getTime();
28224         var sel = date.clearTime().getTime();
28225         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28226         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28227         var ddMatch = this.disabledDatesRE;
28228         var ddText = this.disabledDatesText;
28229         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28230         var ddaysText = this.disabledDaysText;
28231         var format = this.format;
28232
28233         var setCellClass = function(cal, cell){
28234             cell.title = "";
28235             var t = d.getTime();
28236             cell.firstChild.dateValue = t;
28237             if(t == today){
28238                 cell.className += " x-date-today";
28239                 cell.title = cal.todayText;
28240             }
28241             if(t == sel){
28242                 cell.className += " x-date-selected";
28243                 setTimeout(function(){
28244                     try{cell.firstChild.focus();}catch(e){}
28245                 }, 50);
28246             }
28247             // disabling
28248             if(t < min) {
28249                 cell.className = " x-date-disabled";
28250                 cell.title = cal.minText;
28251                 return;
28252             }
28253             if(t > max) {
28254                 cell.className = " x-date-disabled";
28255                 cell.title = cal.maxText;
28256                 return;
28257             }
28258             if(ddays){
28259                 if(ddays.indexOf(d.getDay()) != -1){
28260                     cell.title = ddaysText;
28261                     cell.className = " x-date-disabled";
28262                 }
28263             }
28264             if(ddMatch && format){
28265                 var fvalue = d.dateFormat(format);
28266                 if(ddMatch.test(fvalue)){
28267                     cell.title = ddText.replace("%0", fvalue);
28268                     cell.className = " x-date-disabled";
28269                 }
28270             }
28271         };
28272
28273         var i = 0;
28274         for(; i < startingPos; i++) {
28275             textEls[i].innerHTML = (++prevStart);
28276             d.setDate(d.getDate()+1);
28277             cells[i].className = "x-date-prevday";
28278             setCellClass(this, cells[i]);
28279         }
28280         for(; i < days; i++){
28281             intDay = i - startingPos + 1;
28282             textEls[i].innerHTML = (intDay);
28283             d.setDate(d.getDate()+1);
28284             cells[i].className = "x-date-active";
28285             setCellClass(this, cells[i]);
28286         }
28287         var extraDays = 0;
28288         for(; i < 42; i++) {
28289              textEls[i].innerHTML = (++extraDays);
28290              d.setDate(d.getDate()+1);
28291              cells[i].className = "x-date-nextday";
28292              setCellClass(this, cells[i]);
28293         }
28294
28295         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28296         this.fireEvent('monthchange', this, date);
28297         
28298         if(!this.internalRender){
28299             var main = this.el.dom.firstChild;
28300             var w = main.offsetWidth;
28301             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28302             Roo.fly(main).setWidth(w);
28303             this.internalRender = true;
28304             // opera does not respect the auto grow header center column
28305             // then, after it gets a width opera refuses to recalculate
28306             // without a second pass
28307             if(Roo.isOpera && !this.secondPass){
28308                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28309                 this.secondPass = true;
28310                 this.update.defer(10, this, [date]);
28311             }
28312         }
28313         
28314         
28315     }
28316 });        /*
28317  * Based on:
28318  * Ext JS Library 1.1.1
28319  * Copyright(c) 2006-2007, Ext JS, LLC.
28320  *
28321  * Originally Released Under LGPL - original licence link has changed is not relivant.
28322  *
28323  * Fork - LGPL
28324  * <script type="text/javascript">
28325  */
28326 /**
28327  * @class Roo.TabPanel
28328  * @extends Roo.util.Observable
28329  * A lightweight tab container.
28330  * <br><br>
28331  * Usage:
28332  * <pre><code>
28333 // basic tabs 1, built from existing content
28334 var tabs = new Roo.TabPanel("tabs1");
28335 tabs.addTab("script", "View Script");
28336 tabs.addTab("markup", "View Markup");
28337 tabs.activate("script");
28338
28339 // more advanced tabs, built from javascript
28340 var jtabs = new Roo.TabPanel("jtabs");
28341 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28342
28343 // set up the UpdateManager
28344 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28345 var updater = tab2.getUpdateManager();
28346 updater.setDefaultUrl("ajax1.htm");
28347 tab2.on('activate', updater.refresh, updater, true);
28348
28349 // Use setUrl for Ajax loading
28350 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28351 tab3.setUrl("ajax2.htm", null, true);
28352
28353 // Disabled tab
28354 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28355 tab4.disable();
28356
28357 jtabs.activate("jtabs-1");
28358  * </code></pre>
28359  * @constructor
28360  * Create a new TabPanel.
28361  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28362  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28363  */
28364 Roo.TabPanel = function(container, config){
28365     /**
28366     * The container element for this TabPanel.
28367     * @type Roo.Element
28368     */
28369     this.el = Roo.get(container, true);
28370     if(config){
28371         if(typeof config == "boolean"){
28372             this.tabPosition = config ? "bottom" : "top";
28373         }else{
28374             Roo.apply(this, config);
28375         }
28376     }
28377     if(this.tabPosition == "bottom"){
28378         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28379         this.el.addClass("x-tabs-bottom");
28380     }
28381     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28382     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28383     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28384     if(Roo.isIE){
28385         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28386     }
28387     if(this.tabPosition != "bottom"){
28388         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28389          * @type Roo.Element
28390          */
28391         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28392         this.el.addClass("x-tabs-top");
28393     }
28394     this.items = [];
28395
28396     this.bodyEl.setStyle("position", "relative");
28397
28398     this.active = null;
28399     this.activateDelegate = this.activate.createDelegate(this);
28400
28401     this.addEvents({
28402         /**
28403          * @event tabchange
28404          * Fires when the active tab changes
28405          * @param {Roo.TabPanel} this
28406          * @param {Roo.TabPanelItem} activePanel The new active tab
28407          */
28408         "tabchange": true,
28409         /**
28410          * @event beforetabchange
28411          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28412          * @param {Roo.TabPanel} this
28413          * @param {Object} e Set cancel to true on this object to cancel the tab change
28414          * @param {Roo.TabPanelItem} tab The tab being changed to
28415          */
28416         "beforetabchange" : true
28417     });
28418
28419     Roo.EventManager.onWindowResize(this.onResize, this);
28420     this.cpad = this.el.getPadding("lr");
28421     this.hiddenCount = 0;
28422
28423
28424     // toolbar on the tabbar support...
28425     if (this.toolbar) {
28426         var tcfg = this.toolbar;
28427         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28428         this.toolbar = new Roo.Toolbar(tcfg);
28429         if (Roo.isSafari) {
28430             var tbl = tcfg.container.child('table', true);
28431             tbl.setAttribute('width', '100%');
28432         }
28433         
28434     }
28435    
28436
28437
28438     Roo.TabPanel.superclass.constructor.call(this);
28439 };
28440
28441 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28442     /*
28443      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28444      */
28445     tabPosition : "top",
28446     /*
28447      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28448      */
28449     currentTabWidth : 0,
28450     /*
28451      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28452      */
28453     minTabWidth : 40,
28454     /*
28455      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28456      */
28457     maxTabWidth : 250,
28458     /*
28459      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28460      */
28461     preferredTabWidth : 175,
28462     /*
28463      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28464      */
28465     resizeTabs : false,
28466     /*
28467      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28468      */
28469     monitorResize : true,
28470     /*
28471      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28472      */
28473     toolbar : false,
28474
28475     /**
28476      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28477      * @param {String} id The id of the div to use <b>or create</b>
28478      * @param {String} text The text for the tab
28479      * @param {String} content (optional) Content to put in the TabPanelItem body
28480      * @param {Boolean} closable (optional) True to create a close icon on the tab
28481      * @return {Roo.TabPanelItem} The created TabPanelItem
28482      */
28483     addTab : function(id, text, content, closable){
28484         var item = new Roo.TabPanelItem(this, id, text, closable);
28485         this.addTabItem(item);
28486         if(content){
28487             item.setContent(content);
28488         }
28489         return item;
28490     },
28491
28492     /**
28493      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28494      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28495      * @return {Roo.TabPanelItem}
28496      */
28497     getTab : function(id){
28498         return this.items[id];
28499     },
28500
28501     /**
28502      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28503      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28504      */
28505     hideTab : function(id){
28506         var t = this.items[id];
28507         if(!t.isHidden()){
28508            t.setHidden(true);
28509            this.hiddenCount++;
28510            this.autoSizeTabs();
28511         }
28512     },
28513
28514     /**
28515      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28516      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28517      */
28518     unhideTab : function(id){
28519         var t = this.items[id];
28520         if(t.isHidden()){
28521            t.setHidden(false);
28522            this.hiddenCount--;
28523            this.autoSizeTabs();
28524         }
28525     },
28526
28527     /**
28528      * Adds an existing {@link Roo.TabPanelItem}.
28529      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28530      */
28531     addTabItem : function(item){
28532         this.items[item.id] = item;
28533         this.items.push(item);
28534         if(this.resizeTabs){
28535            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28536            this.autoSizeTabs();
28537         }else{
28538             item.autoSize();
28539         }
28540     },
28541
28542     /**
28543      * Removes a {@link Roo.TabPanelItem}.
28544      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28545      */
28546     removeTab : function(id){
28547         var items = this.items;
28548         var tab = items[id];
28549         if(!tab) { return; }
28550         var index = items.indexOf(tab);
28551         if(this.active == tab && items.length > 1){
28552             var newTab = this.getNextAvailable(index);
28553             if(newTab) {
28554                 newTab.activate();
28555             }
28556         }
28557         this.stripEl.dom.removeChild(tab.pnode.dom);
28558         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28559             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28560         }
28561         items.splice(index, 1);
28562         delete this.items[tab.id];
28563         tab.fireEvent("close", tab);
28564         tab.purgeListeners();
28565         this.autoSizeTabs();
28566     },
28567
28568     getNextAvailable : function(start){
28569         var items = this.items;
28570         var index = start;
28571         // look for a next tab that will slide over to
28572         // replace the one being removed
28573         while(index < items.length){
28574             var item = items[++index];
28575             if(item && !item.isHidden()){
28576                 return item;
28577             }
28578         }
28579         // if one isn't found select the previous tab (on the left)
28580         index = start;
28581         while(index >= 0){
28582             var item = items[--index];
28583             if(item && !item.isHidden()){
28584                 return item;
28585             }
28586         }
28587         return null;
28588     },
28589
28590     /**
28591      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28592      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28593      */
28594     disableTab : function(id){
28595         var tab = this.items[id];
28596         if(tab && this.active != tab){
28597             tab.disable();
28598         }
28599     },
28600
28601     /**
28602      * Enables a {@link Roo.TabPanelItem} that is disabled.
28603      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28604      */
28605     enableTab : function(id){
28606         var tab = this.items[id];
28607         tab.enable();
28608     },
28609
28610     /**
28611      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28612      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28613      * @return {Roo.TabPanelItem} The TabPanelItem.
28614      */
28615     activate : function(id){
28616         var tab = this.items[id];
28617         if(!tab){
28618             return null;
28619         }
28620         if(tab == this.active || tab.disabled){
28621             return tab;
28622         }
28623         var e = {};
28624         this.fireEvent("beforetabchange", this, e, tab);
28625         if(e.cancel !== true && !tab.disabled){
28626             if(this.active){
28627                 this.active.hide();
28628             }
28629             this.active = this.items[id];
28630             this.active.show();
28631             this.fireEvent("tabchange", this, this.active);
28632         }
28633         return tab;
28634     },
28635
28636     /**
28637      * Gets the active {@link Roo.TabPanelItem}.
28638      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28639      */
28640     getActiveTab : function(){
28641         return this.active;
28642     },
28643
28644     /**
28645      * Updates the tab body element to fit the height of the container element
28646      * for overflow scrolling
28647      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28648      */
28649     syncHeight : function(targetHeight){
28650         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28651         var bm = this.bodyEl.getMargins();
28652         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28653         this.bodyEl.setHeight(newHeight);
28654         return newHeight;
28655     },
28656
28657     onResize : function(){
28658         if(this.monitorResize){
28659             this.autoSizeTabs();
28660         }
28661     },
28662
28663     /**
28664      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28665      */
28666     beginUpdate : function(){
28667         this.updating = true;
28668     },
28669
28670     /**
28671      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28672      */
28673     endUpdate : function(){
28674         this.updating = false;
28675         this.autoSizeTabs();
28676     },
28677
28678     /**
28679      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28680      */
28681     autoSizeTabs : function(){
28682         var count = this.items.length;
28683         var vcount = count - this.hiddenCount;
28684         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28685             return;
28686         }
28687         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28688         var availWidth = Math.floor(w / vcount);
28689         var b = this.stripBody;
28690         if(b.getWidth() > w){
28691             var tabs = this.items;
28692             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28693             if(availWidth < this.minTabWidth){
28694                 /*if(!this.sleft){    // incomplete scrolling code
28695                     this.createScrollButtons();
28696                 }
28697                 this.showScroll();
28698                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28699             }
28700         }else{
28701             if(this.currentTabWidth < this.preferredTabWidth){
28702                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28703             }
28704         }
28705     },
28706
28707     /**
28708      * Returns the number of tabs in this TabPanel.
28709      * @return {Number}
28710      */
28711      getCount : function(){
28712          return this.items.length;
28713      },
28714
28715     /**
28716      * Resizes all the tabs to the passed width
28717      * @param {Number} The new width
28718      */
28719     setTabWidth : function(width){
28720         this.currentTabWidth = width;
28721         for(var i = 0, len = this.items.length; i < len; i++) {
28722                 if(!this.items[i].isHidden()) {
28723                 this.items[i].setWidth(width);
28724             }
28725         }
28726     },
28727
28728     /**
28729      * Destroys this TabPanel
28730      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28731      */
28732     destroy : function(removeEl){
28733         Roo.EventManager.removeResizeListener(this.onResize, this);
28734         for(var i = 0, len = this.items.length; i < len; i++){
28735             this.items[i].purgeListeners();
28736         }
28737         if(removeEl === true){
28738             this.el.update("");
28739             this.el.remove();
28740         }
28741     }
28742 });
28743
28744 /**
28745  * @class Roo.TabPanelItem
28746  * @extends Roo.util.Observable
28747  * Represents an individual item (tab plus body) in a TabPanel.
28748  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28749  * @param {String} id The id of this TabPanelItem
28750  * @param {String} text The text for the tab of this TabPanelItem
28751  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28752  */
28753 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28754     /**
28755      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28756      * @type Roo.TabPanel
28757      */
28758     this.tabPanel = tabPanel;
28759     /**
28760      * The id for this TabPanelItem
28761      * @type String
28762      */
28763     this.id = id;
28764     /** @private */
28765     this.disabled = false;
28766     /** @private */
28767     this.text = text;
28768     /** @private */
28769     this.loaded = false;
28770     this.closable = closable;
28771
28772     /**
28773      * The body element for this TabPanelItem.
28774      * @type Roo.Element
28775      */
28776     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28777     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28778     this.bodyEl.setStyle("display", "block");
28779     this.bodyEl.setStyle("zoom", "1");
28780     this.hideAction();
28781
28782     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28783     /** @private */
28784     this.el = Roo.get(els.el, true);
28785     this.inner = Roo.get(els.inner, true);
28786     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28787     this.pnode = Roo.get(els.el.parentNode, true);
28788     this.el.on("mousedown", this.onTabMouseDown, this);
28789     this.el.on("click", this.onTabClick, this);
28790     /** @private */
28791     if(closable){
28792         var c = Roo.get(els.close, true);
28793         c.dom.title = this.closeText;
28794         c.addClassOnOver("close-over");
28795         c.on("click", this.closeClick, this);
28796      }
28797
28798     this.addEvents({
28799          /**
28800          * @event activate
28801          * Fires when this tab becomes the active tab.
28802          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28803          * @param {Roo.TabPanelItem} this
28804          */
28805         "activate": true,
28806         /**
28807          * @event beforeclose
28808          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28809          * @param {Roo.TabPanelItem} this
28810          * @param {Object} e Set cancel to true on this object to cancel the close.
28811          */
28812         "beforeclose": true,
28813         /**
28814          * @event close
28815          * Fires when this tab is closed.
28816          * @param {Roo.TabPanelItem} this
28817          */
28818          "close": true,
28819         /**
28820          * @event deactivate
28821          * Fires when this tab is no longer the active tab.
28822          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28823          * @param {Roo.TabPanelItem} this
28824          */
28825          "deactivate" : true
28826     });
28827     this.hidden = false;
28828
28829     Roo.TabPanelItem.superclass.constructor.call(this);
28830 };
28831
28832 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28833     purgeListeners : function(){
28834        Roo.util.Observable.prototype.purgeListeners.call(this);
28835        this.el.removeAllListeners();
28836     },
28837     /**
28838      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28839      */
28840     show : function(){
28841         this.pnode.addClass("on");
28842         this.showAction();
28843         if(Roo.isOpera){
28844             this.tabPanel.stripWrap.repaint();
28845         }
28846         this.fireEvent("activate", this.tabPanel, this);
28847     },
28848
28849     /**
28850      * Returns true if this tab is the active tab.
28851      * @return {Boolean}
28852      */
28853     isActive : function(){
28854         return this.tabPanel.getActiveTab() == this;
28855     },
28856
28857     /**
28858      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28859      */
28860     hide : function(){
28861         this.pnode.removeClass("on");
28862         this.hideAction();
28863         this.fireEvent("deactivate", this.tabPanel, this);
28864     },
28865
28866     hideAction : function(){
28867         this.bodyEl.hide();
28868         this.bodyEl.setStyle("position", "absolute");
28869         this.bodyEl.setLeft("-20000px");
28870         this.bodyEl.setTop("-20000px");
28871     },
28872
28873     showAction : function(){
28874         this.bodyEl.setStyle("position", "relative");
28875         this.bodyEl.setTop("");
28876         this.bodyEl.setLeft("");
28877         this.bodyEl.show();
28878     },
28879
28880     /**
28881      * Set the tooltip for the tab.
28882      * @param {String} tooltip The tab's tooltip
28883      */
28884     setTooltip : function(text){
28885         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28886             this.textEl.dom.qtip = text;
28887             this.textEl.dom.removeAttribute('title');
28888         }else{
28889             this.textEl.dom.title = text;
28890         }
28891     },
28892
28893     onTabClick : function(e){
28894         e.preventDefault();
28895         this.tabPanel.activate(this.id);
28896     },
28897
28898     onTabMouseDown : function(e){
28899         e.preventDefault();
28900         this.tabPanel.activate(this.id);
28901     },
28902
28903     getWidth : function(){
28904         return this.inner.getWidth();
28905     },
28906
28907     setWidth : function(width){
28908         var iwidth = width - this.pnode.getPadding("lr");
28909         this.inner.setWidth(iwidth);
28910         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28911         this.pnode.setWidth(width);
28912     },
28913
28914     /**
28915      * Show or hide the tab
28916      * @param {Boolean} hidden True to hide or false to show.
28917      */
28918     setHidden : function(hidden){
28919         this.hidden = hidden;
28920         this.pnode.setStyle("display", hidden ? "none" : "");
28921     },
28922
28923     /**
28924      * Returns true if this tab is "hidden"
28925      * @return {Boolean}
28926      */
28927     isHidden : function(){
28928         return this.hidden;
28929     },
28930
28931     /**
28932      * Returns the text for this tab
28933      * @return {String}
28934      */
28935     getText : function(){
28936         return this.text;
28937     },
28938
28939     autoSize : function(){
28940         //this.el.beginMeasure();
28941         this.textEl.setWidth(1);
28942         /*
28943          *  #2804 [new] Tabs in Roojs
28944          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28945          */
28946         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28947         //this.el.endMeasure();
28948     },
28949
28950     /**
28951      * Sets the text for the tab (Note: this also sets the tooltip text)
28952      * @param {String} text The tab's text and tooltip
28953      */
28954     setText : function(text){
28955         this.text = text;
28956         this.textEl.update(text);
28957         this.setTooltip(text);
28958         if(!this.tabPanel.resizeTabs){
28959             this.autoSize();
28960         }
28961     },
28962     /**
28963      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28964      */
28965     activate : function(){
28966         this.tabPanel.activate(this.id);
28967     },
28968
28969     /**
28970      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28971      */
28972     disable : function(){
28973         if(this.tabPanel.active != this){
28974             this.disabled = true;
28975             this.pnode.addClass("disabled");
28976         }
28977     },
28978
28979     /**
28980      * Enables this TabPanelItem if it was previously disabled.
28981      */
28982     enable : function(){
28983         this.disabled = false;
28984         this.pnode.removeClass("disabled");
28985     },
28986
28987     /**
28988      * Sets the content for this TabPanelItem.
28989      * @param {String} content The content
28990      * @param {Boolean} loadScripts true to look for and load scripts
28991      */
28992     setContent : function(content, loadScripts){
28993         this.bodyEl.update(content, loadScripts);
28994     },
28995
28996     /**
28997      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28998      * @return {Roo.UpdateManager} The UpdateManager
28999      */
29000     getUpdateManager : function(){
29001         return this.bodyEl.getUpdateManager();
29002     },
29003
29004     /**
29005      * Set a URL to be used to load the content for this TabPanelItem.
29006      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29007      * @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)
29008      * @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)
29009      * @return {Roo.UpdateManager} The UpdateManager
29010      */
29011     setUrl : function(url, params, loadOnce){
29012         if(this.refreshDelegate){
29013             this.un('activate', this.refreshDelegate);
29014         }
29015         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29016         this.on("activate", this.refreshDelegate);
29017         return this.bodyEl.getUpdateManager();
29018     },
29019
29020     /** @private */
29021     _handleRefresh : function(url, params, loadOnce){
29022         if(!loadOnce || !this.loaded){
29023             var updater = this.bodyEl.getUpdateManager();
29024             updater.update(url, params, this._setLoaded.createDelegate(this));
29025         }
29026     },
29027
29028     /**
29029      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29030      *   Will fail silently if the setUrl method has not been called.
29031      *   This does not activate the panel, just updates its content.
29032      */
29033     refresh : function(){
29034         if(this.refreshDelegate){
29035            this.loaded = false;
29036            this.refreshDelegate();
29037         }
29038     },
29039
29040     /** @private */
29041     _setLoaded : function(){
29042         this.loaded = true;
29043     },
29044
29045     /** @private */
29046     closeClick : function(e){
29047         var o = {};
29048         e.stopEvent();
29049         this.fireEvent("beforeclose", this, o);
29050         if(o.cancel !== true){
29051             this.tabPanel.removeTab(this.id);
29052         }
29053     },
29054     /**
29055      * The text displayed in the tooltip for the close icon.
29056      * @type String
29057      */
29058     closeText : "Close this tab"
29059 });
29060
29061 /** @private */
29062 Roo.TabPanel.prototype.createStrip = function(container){
29063     var strip = document.createElement("div");
29064     strip.className = "x-tabs-wrap";
29065     container.appendChild(strip);
29066     return strip;
29067 };
29068 /** @private */
29069 Roo.TabPanel.prototype.createStripList = function(strip){
29070     // div wrapper for retard IE
29071     // returns the "tr" element.
29072     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29073         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29074         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29075     return strip.firstChild.firstChild.firstChild.firstChild;
29076 };
29077 /** @private */
29078 Roo.TabPanel.prototype.createBody = function(container){
29079     var body = document.createElement("div");
29080     Roo.id(body, "tab-body");
29081     Roo.fly(body).addClass("x-tabs-body");
29082     container.appendChild(body);
29083     return body;
29084 };
29085 /** @private */
29086 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29087     var body = Roo.getDom(id);
29088     if(!body){
29089         body = document.createElement("div");
29090         body.id = id;
29091     }
29092     Roo.fly(body).addClass("x-tabs-item-body");
29093     bodyEl.insertBefore(body, bodyEl.firstChild);
29094     return body;
29095 };
29096 /** @private */
29097 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29098     var td = document.createElement("td");
29099     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29100     //stripEl.appendChild(td);
29101     if(closable){
29102         td.className = "x-tabs-closable";
29103         if(!this.closeTpl){
29104             this.closeTpl = new Roo.Template(
29105                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29106                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29107                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29108             );
29109         }
29110         var el = this.closeTpl.overwrite(td, {"text": text});
29111         var close = el.getElementsByTagName("div")[0];
29112         var inner = el.getElementsByTagName("em")[0];
29113         return {"el": el, "close": close, "inner": inner};
29114     } else {
29115         if(!this.tabTpl){
29116             this.tabTpl = new Roo.Template(
29117                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29118                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29119             );
29120         }
29121         var el = this.tabTpl.overwrite(td, {"text": text});
29122         var inner = el.getElementsByTagName("em")[0];
29123         return {"el": el, "inner": inner};
29124     }
29125 };/*
29126  * Based on:
29127  * Ext JS Library 1.1.1
29128  * Copyright(c) 2006-2007, Ext JS, LLC.
29129  *
29130  * Originally Released Under LGPL - original licence link has changed is not relivant.
29131  *
29132  * Fork - LGPL
29133  * <script type="text/javascript">
29134  */
29135
29136 /**
29137  * @class Roo.Button
29138  * @extends Roo.util.Observable
29139  * Simple Button class
29140  * @cfg {String} text The button text
29141  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29142  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29143  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29144  * @cfg {Object} scope The scope of the handler
29145  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29146  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29147  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29148  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29149  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29150  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29151    applies if enableToggle = true)
29152  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29153  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29154   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29155  * @constructor
29156  * Create a new button
29157  * @param {Object} config The config object
29158  */
29159 Roo.Button = function(renderTo, config)
29160 {
29161     if (!config) {
29162         config = renderTo;
29163         renderTo = config.renderTo || false;
29164     }
29165     
29166     Roo.apply(this, config);
29167     this.addEvents({
29168         /**
29169              * @event click
29170              * Fires when this button is clicked
29171              * @param {Button} this
29172              * @param {EventObject} e The click event
29173              */
29174             "click" : true,
29175         /**
29176              * @event toggle
29177              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29178              * @param {Button} this
29179              * @param {Boolean} pressed
29180              */
29181             "toggle" : true,
29182         /**
29183              * @event mouseover
29184              * Fires when the mouse hovers over the button
29185              * @param {Button} this
29186              * @param {Event} e The event object
29187              */
29188         'mouseover' : true,
29189         /**
29190              * @event mouseout
29191              * Fires when the mouse exits the button
29192              * @param {Button} this
29193              * @param {Event} e The event object
29194              */
29195         'mouseout': true,
29196          /**
29197              * @event render
29198              * Fires when the button is rendered
29199              * @param {Button} this
29200              */
29201         'render': true
29202     });
29203     if(this.menu){
29204         this.menu = Roo.menu.MenuMgr.get(this.menu);
29205     }
29206     // register listeners first!!  - so render can be captured..
29207     Roo.util.Observable.call(this);
29208     if(renderTo){
29209         this.render(renderTo);
29210     }
29211     
29212   
29213 };
29214
29215 Roo.extend(Roo.Button, Roo.util.Observable, {
29216     /**
29217      * 
29218      */
29219     
29220     /**
29221      * Read-only. True if this button is hidden
29222      * @type Boolean
29223      */
29224     hidden : false,
29225     /**
29226      * Read-only. True if this button is disabled
29227      * @type Boolean
29228      */
29229     disabled : false,
29230     /**
29231      * Read-only. True if this button is pressed (only if enableToggle = true)
29232      * @type Boolean
29233      */
29234     pressed : false,
29235
29236     /**
29237      * @cfg {Number} tabIndex 
29238      * The DOM tabIndex for this button (defaults to undefined)
29239      */
29240     tabIndex : undefined,
29241
29242     /**
29243      * @cfg {Boolean} enableToggle
29244      * True to enable pressed/not pressed toggling (defaults to false)
29245      */
29246     enableToggle: false,
29247     /**
29248      * @cfg {Mixed} menu
29249      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29250      */
29251     menu : undefined,
29252     /**
29253      * @cfg {String} menuAlign
29254      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29255      */
29256     menuAlign : "tl-bl?",
29257
29258     /**
29259      * @cfg {String} iconCls
29260      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29261      */
29262     iconCls : undefined,
29263     /**
29264      * @cfg {String} type
29265      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29266      */
29267     type : 'button',
29268
29269     // private
29270     menuClassTarget: 'tr',
29271
29272     /**
29273      * @cfg {String} clickEvent
29274      * The type of event to map to the button's event handler (defaults to 'click')
29275      */
29276     clickEvent : 'click',
29277
29278     /**
29279      * @cfg {Boolean} handleMouseEvents
29280      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29281      */
29282     handleMouseEvents : true,
29283
29284     /**
29285      * @cfg {String} tooltipType
29286      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29287      */
29288     tooltipType : 'qtip',
29289
29290     /**
29291      * @cfg {String} cls
29292      * A CSS class to apply to the button's main element.
29293      */
29294     
29295     /**
29296      * @cfg {Roo.Template} template (Optional)
29297      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29298      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29299      * require code modifications if required elements (e.g. a button) aren't present.
29300      */
29301
29302     // private
29303     render : function(renderTo){
29304         var btn;
29305         if(this.hideParent){
29306             this.parentEl = Roo.get(renderTo);
29307         }
29308         if(!this.dhconfig){
29309             if(!this.template){
29310                 if(!Roo.Button.buttonTemplate){
29311                     // hideous table template
29312                     Roo.Button.buttonTemplate = new Roo.Template(
29313                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29314                         '<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>',
29315                         "</tr></tbody></table>");
29316                 }
29317                 this.template = Roo.Button.buttonTemplate;
29318             }
29319             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29320             var btnEl = btn.child("button:first");
29321             btnEl.on('focus', this.onFocus, this);
29322             btnEl.on('blur', this.onBlur, this);
29323             if(this.cls){
29324                 btn.addClass(this.cls);
29325             }
29326             if(this.icon){
29327                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29328             }
29329             if(this.iconCls){
29330                 btnEl.addClass(this.iconCls);
29331                 if(!this.cls){
29332                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29333                 }
29334             }
29335             if(this.tabIndex !== undefined){
29336                 btnEl.dom.tabIndex = this.tabIndex;
29337             }
29338             if(this.tooltip){
29339                 if(typeof this.tooltip == 'object'){
29340                     Roo.QuickTips.tips(Roo.apply({
29341                           target: btnEl.id
29342                     }, this.tooltip));
29343                 } else {
29344                     btnEl.dom[this.tooltipType] = this.tooltip;
29345                 }
29346             }
29347         }else{
29348             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29349         }
29350         this.el = btn;
29351         if(this.id){
29352             this.el.dom.id = this.el.id = this.id;
29353         }
29354         if(this.menu){
29355             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29356             this.menu.on("show", this.onMenuShow, this);
29357             this.menu.on("hide", this.onMenuHide, this);
29358         }
29359         btn.addClass("x-btn");
29360         if(Roo.isIE && !Roo.isIE7){
29361             this.autoWidth.defer(1, this);
29362         }else{
29363             this.autoWidth();
29364         }
29365         if(this.handleMouseEvents){
29366             btn.on("mouseover", this.onMouseOver, this);
29367             btn.on("mouseout", this.onMouseOut, this);
29368             btn.on("mousedown", this.onMouseDown, this);
29369         }
29370         btn.on(this.clickEvent, this.onClick, this);
29371         //btn.on("mouseup", this.onMouseUp, this);
29372         if(this.hidden){
29373             this.hide();
29374         }
29375         if(this.disabled){
29376             this.disable();
29377         }
29378         Roo.ButtonToggleMgr.register(this);
29379         if(this.pressed){
29380             this.el.addClass("x-btn-pressed");
29381         }
29382         if(this.repeat){
29383             var repeater = new Roo.util.ClickRepeater(btn,
29384                 typeof this.repeat == "object" ? this.repeat : {}
29385             );
29386             repeater.on("click", this.onClick,  this);
29387         }
29388         
29389         this.fireEvent('render', this);
29390         
29391     },
29392     /**
29393      * Returns the button's underlying element
29394      * @return {Roo.Element} The element
29395      */
29396     getEl : function(){
29397         return this.el;  
29398     },
29399     
29400     /**
29401      * Destroys this Button and removes any listeners.
29402      */
29403     destroy : function(){
29404         Roo.ButtonToggleMgr.unregister(this);
29405         this.el.removeAllListeners();
29406         this.purgeListeners();
29407         this.el.remove();
29408     },
29409
29410     // private
29411     autoWidth : function(){
29412         if(this.el){
29413             this.el.setWidth("auto");
29414             if(Roo.isIE7 && Roo.isStrict){
29415                 var ib = this.el.child('button');
29416                 if(ib && ib.getWidth() > 20){
29417                     ib.clip();
29418                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29419                 }
29420             }
29421             if(this.minWidth){
29422                 if(this.hidden){
29423                     this.el.beginMeasure();
29424                 }
29425                 if(this.el.getWidth() < this.minWidth){
29426                     this.el.setWidth(this.minWidth);
29427                 }
29428                 if(this.hidden){
29429                     this.el.endMeasure();
29430                 }
29431             }
29432         }
29433     },
29434
29435     /**
29436      * Assigns this button's click handler
29437      * @param {Function} handler The function to call when the button is clicked
29438      * @param {Object} scope (optional) Scope for the function passed in
29439      */
29440     setHandler : function(handler, scope){
29441         this.handler = handler;
29442         this.scope = scope;  
29443     },
29444     
29445     /**
29446      * Sets this button's text
29447      * @param {String} text The button text
29448      */
29449     setText : function(text){
29450         this.text = text;
29451         if(this.el){
29452             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29453         }
29454         this.autoWidth();
29455     },
29456     
29457     /**
29458      * Gets the text for this button
29459      * @return {String} The button text
29460      */
29461     getText : function(){
29462         return this.text;  
29463     },
29464     
29465     /**
29466      * Show this button
29467      */
29468     show: function(){
29469         this.hidden = false;
29470         if(this.el){
29471             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29472         }
29473     },
29474     
29475     /**
29476      * Hide this button
29477      */
29478     hide: function(){
29479         this.hidden = true;
29480         if(this.el){
29481             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29482         }
29483     },
29484     
29485     /**
29486      * Convenience function for boolean show/hide
29487      * @param {Boolean} visible True to show, false to hide
29488      */
29489     setVisible: function(visible){
29490         if(visible) {
29491             this.show();
29492         }else{
29493             this.hide();
29494         }
29495     },
29496     
29497     /**
29498      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29499      * @param {Boolean} state (optional) Force a particular state
29500      */
29501     toggle : function(state){
29502         state = state === undefined ? !this.pressed : state;
29503         if(state != this.pressed){
29504             if(state){
29505                 this.el.addClass("x-btn-pressed");
29506                 this.pressed = true;
29507                 this.fireEvent("toggle", this, true);
29508             }else{
29509                 this.el.removeClass("x-btn-pressed");
29510                 this.pressed = false;
29511                 this.fireEvent("toggle", this, false);
29512             }
29513             if(this.toggleHandler){
29514                 this.toggleHandler.call(this.scope || this, this, state);
29515             }
29516         }
29517     },
29518     
29519     /**
29520      * Focus the button
29521      */
29522     focus : function(){
29523         this.el.child('button:first').focus();
29524     },
29525     
29526     /**
29527      * Disable this button
29528      */
29529     disable : function(){
29530         if(this.el){
29531             this.el.addClass("x-btn-disabled");
29532         }
29533         this.disabled = true;
29534     },
29535     
29536     /**
29537      * Enable this button
29538      */
29539     enable : function(){
29540         if(this.el){
29541             this.el.removeClass("x-btn-disabled");
29542         }
29543         this.disabled = false;
29544     },
29545
29546     /**
29547      * Convenience function for boolean enable/disable
29548      * @param {Boolean} enabled True to enable, false to disable
29549      */
29550     setDisabled : function(v){
29551         this[v !== true ? "enable" : "disable"]();
29552     },
29553
29554     // private
29555     onClick : function(e)
29556     {
29557         if(e){
29558             e.preventDefault();
29559         }
29560         if(e.button != 0){
29561             return;
29562         }
29563         if(!this.disabled){
29564             if(this.enableToggle){
29565                 this.toggle();
29566             }
29567             if(this.menu && !this.menu.isVisible()){
29568                 this.menu.show(this.el, this.menuAlign);
29569             }
29570             this.fireEvent("click", this, e);
29571             if(this.handler){
29572                 this.el.removeClass("x-btn-over");
29573                 this.handler.call(this.scope || this, this, e);
29574             }
29575         }
29576     },
29577     // private
29578     onMouseOver : function(e){
29579         if(!this.disabled){
29580             this.el.addClass("x-btn-over");
29581             this.fireEvent('mouseover', this, e);
29582         }
29583     },
29584     // private
29585     onMouseOut : function(e){
29586         if(!e.within(this.el,  true)){
29587             this.el.removeClass("x-btn-over");
29588             this.fireEvent('mouseout', this, e);
29589         }
29590     },
29591     // private
29592     onFocus : function(e){
29593         if(!this.disabled){
29594             this.el.addClass("x-btn-focus");
29595         }
29596     },
29597     // private
29598     onBlur : function(e){
29599         this.el.removeClass("x-btn-focus");
29600     },
29601     // private
29602     onMouseDown : function(e){
29603         if(!this.disabled && e.button == 0){
29604             this.el.addClass("x-btn-click");
29605             Roo.get(document).on('mouseup', this.onMouseUp, this);
29606         }
29607     },
29608     // private
29609     onMouseUp : function(e){
29610         if(e.button == 0){
29611             this.el.removeClass("x-btn-click");
29612             Roo.get(document).un('mouseup', this.onMouseUp, this);
29613         }
29614     },
29615     // private
29616     onMenuShow : function(e){
29617         this.el.addClass("x-btn-menu-active");
29618     },
29619     // private
29620     onMenuHide : function(e){
29621         this.el.removeClass("x-btn-menu-active");
29622     }   
29623 });
29624
29625 // Private utility class used by Button
29626 Roo.ButtonToggleMgr = function(){
29627    var groups = {};
29628    
29629    function toggleGroup(btn, state){
29630        if(state){
29631            var g = groups[btn.toggleGroup];
29632            for(var i = 0, l = g.length; i < l; i++){
29633                if(g[i] != btn){
29634                    g[i].toggle(false);
29635                }
29636            }
29637        }
29638    }
29639    
29640    return {
29641        register : function(btn){
29642            if(!btn.toggleGroup){
29643                return;
29644            }
29645            var g = groups[btn.toggleGroup];
29646            if(!g){
29647                g = groups[btn.toggleGroup] = [];
29648            }
29649            g.push(btn);
29650            btn.on("toggle", toggleGroup);
29651        },
29652        
29653        unregister : function(btn){
29654            if(!btn.toggleGroup){
29655                return;
29656            }
29657            var g = groups[btn.toggleGroup];
29658            if(g){
29659                g.remove(btn);
29660                btn.un("toggle", toggleGroup);
29661            }
29662        }
29663    };
29664 }();/*
29665  * Based on:
29666  * Ext JS Library 1.1.1
29667  * Copyright(c) 2006-2007, Ext JS, LLC.
29668  *
29669  * Originally Released Under LGPL - original licence link has changed is not relivant.
29670  *
29671  * Fork - LGPL
29672  * <script type="text/javascript">
29673  */
29674  
29675 /**
29676  * @class Roo.SplitButton
29677  * @extends Roo.Button
29678  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29679  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29680  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29681  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29682  * @cfg {String} arrowTooltip The title attribute of the arrow
29683  * @constructor
29684  * Create a new menu button
29685  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29686  * @param {Object} config The config object
29687  */
29688 Roo.SplitButton = function(renderTo, config){
29689     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29690     /**
29691      * @event arrowclick
29692      * Fires when this button's arrow is clicked
29693      * @param {SplitButton} this
29694      * @param {EventObject} e The click event
29695      */
29696     this.addEvents({"arrowclick":true});
29697 };
29698
29699 Roo.extend(Roo.SplitButton, Roo.Button, {
29700     render : function(renderTo){
29701         // this is one sweet looking template!
29702         var tpl = new Roo.Template(
29703             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29704             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29705             '<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>',
29706             "</tbody></table></td><td>",
29707             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29708             '<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>',
29709             "</tbody></table></td></tr></table>"
29710         );
29711         var btn = tpl.append(renderTo, [this.text, this.type], true);
29712         var btnEl = btn.child("button");
29713         if(this.cls){
29714             btn.addClass(this.cls);
29715         }
29716         if(this.icon){
29717             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29718         }
29719         if(this.iconCls){
29720             btnEl.addClass(this.iconCls);
29721             if(!this.cls){
29722                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29723             }
29724         }
29725         this.el = btn;
29726         if(this.handleMouseEvents){
29727             btn.on("mouseover", this.onMouseOver, this);
29728             btn.on("mouseout", this.onMouseOut, this);
29729             btn.on("mousedown", this.onMouseDown, this);
29730             btn.on("mouseup", this.onMouseUp, this);
29731         }
29732         btn.on(this.clickEvent, this.onClick, this);
29733         if(this.tooltip){
29734             if(typeof this.tooltip == 'object'){
29735                 Roo.QuickTips.tips(Roo.apply({
29736                       target: btnEl.id
29737                 }, this.tooltip));
29738             } else {
29739                 btnEl.dom[this.tooltipType] = this.tooltip;
29740             }
29741         }
29742         if(this.arrowTooltip){
29743             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29744         }
29745         if(this.hidden){
29746             this.hide();
29747         }
29748         if(this.disabled){
29749             this.disable();
29750         }
29751         if(this.pressed){
29752             this.el.addClass("x-btn-pressed");
29753         }
29754         if(Roo.isIE && !Roo.isIE7){
29755             this.autoWidth.defer(1, this);
29756         }else{
29757             this.autoWidth();
29758         }
29759         if(this.menu){
29760             this.menu.on("show", this.onMenuShow, this);
29761             this.menu.on("hide", this.onMenuHide, this);
29762         }
29763         this.fireEvent('render', this);
29764     },
29765
29766     // private
29767     autoWidth : function(){
29768         if(this.el){
29769             var tbl = this.el.child("table:first");
29770             var tbl2 = this.el.child("table:last");
29771             this.el.setWidth("auto");
29772             tbl.setWidth("auto");
29773             if(Roo.isIE7 && Roo.isStrict){
29774                 var ib = this.el.child('button:first');
29775                 if(ib && ib.getWidth() > 20){
29776                     ib.clip();
29777                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29778                 }
29779             }
29780             if(this.minWidth){
29781                 if(this.hidden){
29782                     this.el.beginMeasure();
29783                 }
29784                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29785                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29786                 }
29787                 if(this.hidden){
29788                     this.el.endMeasure();
29789                 }
29790             }
29791             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29792         } 
29793     },
29794     /**
29795      * Sets this button's click handler
29796      * @param {Function} handler The function to call when the button is clicked
29797      * @param {Object} scope (optional) Scope for the function passed above
29798      */
29799     setHandler : function(handler, scope){
29800         this.handler = handler;
29801         this.scope = scope;  
29802     },
29803     
29804     /**
29805      * Sets this button's arrow click handler
29806      * @param {Function} handler The function to call when the arrow is clicked
29807      * @param {Object} scope (optional) Scope for the function passed above
29808      */
29809     setArrowHandler : function(handler, scope){
29810         this.arrowHandler = handler;
29811         this.scope = scope;  
29812     },
29813     
29814     /**
29815      * Focus the button
29816      */
29817     focus : function(){
29818         if(this.el){
29819             this.el.child("button:first").focus();
29820         }
29821     },
29822
29823     // private
29824     onClick : function(e){
29825         e.preventDefault();
29826         if(!this.disabled){
29827             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29828                 if(this.menu && !this.menu.isVisible()){
29829                     this.menu.show(this.el, this.menuAlign);
29830                 }
29831                 this.fireEvent("arrowclick", this, e);
29832                 if(this.arrowHandler){
29833                     this.arrowHandler.call(this.scope || this, this, e);
29834                 }
29835             }else{
29836                 this.fireEvent("click", this, e);
29837                 if(this.handler){
29838                     this.handler.call(this.scope || this, this, e);
29839                 }
29840             }
29841         }
29842     },
29843     // private
29844     onMouseDown : function(e){
29845         if(!this.disabled){
29846             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29847         }
29848     },
29849     // private
29850     onMouseUp : function(e){
29851         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29852     }   
29853 });
29854
29855
29856 // backwards compat
29857 Roo.MenuButton = Roo.SplitButton;/*
29858  * Based on:
29859  * Ext JS Library 1.1.1
29860  * Copyright(c) 2006-2007, Ext JS, LLC.
29861  *
29862  * Originally Released Under LGPL - original licence link has changed is not relivant.
29863  *
29864  * Fork - LGPL
29865  * <script type="text/javascript">
29866  */
29867
29868 /**
29869  * @class Roo.Toolbar
29870  * Basic Toolbar class.
29871  * @constructor
29872  * Creates a new Toolbar
29873  * @param {Object} container The config object
29874  */ 
29875 Roo.Toolbar = function(container, buttons, config)
29876 {
29877     /// old consturctor format still supported..
29878     if(container instanceof Array){ // omit the container for later rendering
29879         buttons = container;
29880         config = buttons;
29881         container = null;
29882     }
29883     if (typeof(container) == 'object' && container.xtype) {
29884         config = container;
29885         container = config.container;
29886         buttons = config.buttons || []; // not really - use items!!
29887     }
29888     var xitems = [];
29889     if (config && config.items) {
29890         xitems = config.items;
29891         delete config.items;
29892     }
29893     Roo.apply(this, config);
29894     this.buttons = buttons;
29895     
29896     if(container){
29897         this.render(container);
29898     }
29899     this.xitems = xitems;
29900     Roo.each(xitems, function(b) {
29901         this.add(b);
29902     }, this);
29903     
29904 };
29905
29906 Roo.Toolbar.prototype = {
29907     /**
29908      * @cfg {Array} items
29909      * array of button configs or elements to add (will be converted to a MixedCollection)
29910      */
29911     
29912     /**
29913      * @cfg {String/HTMLElement/Element} container
29914      * The id or element that will contain the toolbar
29915      */
29916     // private
29917     render : function(ct){
29918         this.el = Roo.get(ct);
29919         if(this.cls){
29920             this.el.addClass(this.cls);
29921         }
29922         // using a table allows for vertical alignment
29923         // 100% width is needed by Safari...
29924         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29925         this.tr = this.el.child("tr", true);
29926         var autoId = 0;
29927         this.items = new Roo.util.MixedCollection(false, function(o){
29928             return o.id || ("item" + (++autoId));
29929         });
29930         if(this.buttons){
29931             this.add.apply(this, this.buttons);
29932             delete this.buttons;
29933         }
29934     },
29935
29936     /**
29937      * Adds element(s) to the toolbar -- this function takes a variable number of 
29938      * arguments of mixed type and adds them to the toolbar.
29939      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29940      * <ul>
29941      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29942      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29943      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29944      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29945      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29946      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29947      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29948      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29949      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29950      * </ul>
29951      * @param {Mixed} arg2
29952      * @param {Mixed} etc.
29953      */
29954     add : function(){
29955         var a = arguments, l = a.length;
29956         for(var i = 0; i < l; i++){
29957             this._add(a[i]);
29958         }
29959     },
29960     // private..
29961     _add : function(el) {
29962         
29963         if (el.xtype) {
29964             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29965         }
29966         
29967         if (el.applyTo){ // some kind of form field
29968             return this.addField(el);
29969         } 
29970         if (el.render){ // some kind of Toolbar.Item
29971             return this.addItem(el);
29972         }
29973         if (typeof el == "string"){ // string
29974             if(el == "separator" || el == "-"){
29975                 return this.addSeparator();
29976             }
29977             if (el == " "){
29978                 return this.addSpacer();
29979             }
29980             if(el == "->"){
29981                 return this.addFill();
29982             }
29983             return this.addText(el);
29984             
29985         }
29986         if(el.tagName){ // element
29987             return this.addElement(el);
29988         }
29989         if(typeof el == "object"){ // must be button config?
29990             return this.addButton(el);
29991         }
29992         // and now what?!?!
29993         return false;
29994         
29995     },
29996     
29997     /**
29998      * Add an Xtype element
29999      * @param {Object} xtype Xtype Object
30000      * @return {Object} created Object
30001      */
30002     addxtype : function(e){
30003         return this.add(e);  
30004     },
30005     
30006     /**
30007      * Returns the Element for this toolbar.
30008      * @return {Roo.Element}
30009      */
30010     getEl : function(){
30011         return this.el;  
30012     },
30013     
30014     /**
30015      * Adds a separator
30016      * @return {Roo.Toolbar.Item} The separator item
30017      */
30018     addSeparator : function(){
30019         return this.addItem(new Roo.Toolbar.Separator());
30020     },
30021
30022     /**
30023      * Adds a spacer element
30024      * @return {Roo.Toolbar.Spacer} The spacer item
30025      */
30026     addSpacer : function(){
30027         return this.addItem(new Roo.Toolbar.Spacer());
30028     },
30029
30030     /**
30031      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30032      * @return {Roo.Toolbar.Fill} The fill item
30033      */
30034     addFill : function(){
30035         return this.addItem(new Roo.Toolbar.Fill());
30036     },
30037
30038     /**
30039      * Adds any standard HTML element to the toolbar
30040      * @param {String/HTMLElement/Element} el The element or id of the element to add
30041      * @return {Roo.Toolbar.Item} The element's item
30042      */
30043     addElement : function(el){
30044         return this.addItem(new Roo.Toolbar.Item(el));
30045     },
30046     /**
30047      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30048      * @type Roo.util.MixedCollection  
30049      */
30050     items : false,
30051      
30052     /**
30053      * Adds any Toolbar.Item or subclass
30054      * @param {Roo.Toolbar.Item} item
30055      * @return {Roo.Toolbar.Item} The item
30056      */
30057     addItem : function(item){
30058         var td = this.nextBlock();
30059         item.render(td);
30060         this.items.add(item);
30061         return item;
30062     },
30063     
30064     /**
30065      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30066      * @param {Object/Array} config A button config or array of configs
30067      * @return {Roo.Toolbar.Button/Array}
30068      */
30069     addButton : function(config){
30070         if(config instanceof Array){
30071             var buttons = [];
30072             for(var i = 0, len = config.length; i < len; i++) {
30073                 buttons.push(this.addButton(config[i]));
30074             }
30075             return buttons;
30076         }
30077         var b = config;
30078         if(!(config instanceof Roo.Toolbar.Button)){
30079             b = config.split ?
30080                 new Roo.Toolbar.SplitButton(config) :
30081                 new Roo.Toolbar.Button(config);
30082         }
30083         var td = this.nextBlock();
30084         b.render(td);
30085         this.items.add(b);
30086         return b;
30087     },
30088     
30089     /**
30090      * Adds text to the toolbar
30091      * @param {String} text The text to add
30092      * @return {Roo.Toolbar.Item} The element's item
30093      */
30094     addText : function(text){
30095         return this.addItem(new Roo.Toolbar.TextItem(text));
30096     },
30097     
30098     /**
30099      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30100      * @param {Number} index The index where the item is to be inserted
30101      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30102      * @return {Roo.Toolbar.Button/Item}
30103      */
30104     insertButton : function(index, item){
30105         if(item instanceof Array){
30106             var buttons = [];
30107             for(var i = 0, len = item.length; i < len; i++) {
30108                buttons.push(this.insertButton(index + i, item[i]));
30109             }
30110             return buttons;
30111         }
30112         if (!(item instanceof Roo.Toolbar.Button)){
30113            item = new Roo.Toolbar.Button(item);
30114         }
30115         var td = document.createElement("td");
30116         this.tr.insertBefore(td, this.tr.childNodes[index]);
30117         item.render(td);
30118         this.items.insert(index, item);
30119         return item;
30120     },
30121     
30122     /**
30123      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30124      * @param {Object} config
30125      * @return {Roo.Toolbar.Item} The element's item
30126      */
30127     addDom : function(config, returnEl){
30128         var td = this.nextBlock();
30129         Roo.DomHelper.overwrite(td, config);
30130         var ti = new Roo.Toolbar.Item(td.firstChild);
30131         ti.render(td);
30132         this.items.add(ti);
30133         return ti;
30134     },
30135
30136     /**
30137      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30138      * @type Roo.util.MixedCollection  
30139      */
30140     fields : false,
30141     
30142     /**
30143      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30144      * Note: the field should not have been rendered yet. For a field that has already been
30145      * rendered, use {@link #addElement}.
30146      * @param {Roo.form.Field} field
30147      * @return {Roo.ToolbarItem}
30148      */
30149      
30150       
30151     addField : function(field) {
30152         if (!this.fields) {
30153             var autoId = 0;
30154             this.fields = new Roo.util.MixedCollection(false, function(o){
30155                 return o.id || ("item" + (++autoId));
30156             });
30157
30158         }
30159         
30160         var td = this.nextBlock();
30161         field.render(td);
30162         var ti = new Roo.Toolbar.Item(td.firstChild);
30163         ti.render(td);
30164         this.items.add(ti);
30165         this.fields.add(field);
30166         return ti;
30167     },
30168     /**
30169      * Hide the toolbar
30170      * @method hide
30171      */
30172      
30173       
30174     hide : function()
30175     {
30176         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30177         this.el.child('div').hide();
30178     },
30179     /**
30180      * Show the toolbar
30181      * @method show
30182      */
30183     show : function()
30184     {
30185         this.el.child('div').show();
30186     },
30187       
30188     // private
30189     nextBlock : function(){
30190         var td = document.createElement("td");
30191         this.tr.appendChild(td);
30192         return td;
30193     },
30194
30195     // private
30196     destroy : function(){
30197         if(this.items){ // rendered?
30198             Roo.destroy.apply(Roo, this.items.items);
30199         }
30200         if(this.fields){ // rendered?
30201             Roo.destroy.apply(Roo, this.fields.items);
30202         }
30203         Roo.Element.uncache(this.el, this.tr);
30204     }
30205 };
30206
30207 /**
30208  * @class Roo.Toolbar.Item
30209  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30210  * @constructor
30211  * Creates a new Item
30212  * @param {HTMLElement} el 
30213  */
30214 Roo.Toolbar.Item = function(el){
30215     var cfg = {};
30216     if (typeof (el.xtype) != 'undefined') {
30217         cfg = el;
30218         el = cfg.el;
30219     }
30220     
30221     this.el = Roo.getDom(el);
30222     this.id = Roo.id(this.el);
30223     this.hidden = false;
30224     
30225     this.addEvents({
30226          /**
30227              * @event render
30228              * Fires when the button is rendered
30229              * @param {Button} this
30230              */
30231         'render': true
30232     });
30233     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30234 };
30235 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30236 //Roo.Toolbar.Item.prototype = {
30237     
30238     /**
30239      * Get this item's HTML Element
30240      * @return {HTMLElement}
30241      */
30242     getEl : function(){
30243        return this.el;  
30244     },
30245
30246     // private
30247     render : function(td){
30248         
30249          this.td = td;
30250         td.appendChild(this.el);
30251         
30252         this.fireEvent('render', this);
30253     },
30254     
30255     /**
30256      * Removes and destroys this item.
30257      */
30258     destroy : function(){
30259         this.td.parentNode.removeChild(this.td);
30260     },
30261     
30262     /**
30263      * Shows this item.
30264      */
30265     show: function(){
30266         this.hidden = false;
30267         this.td.style.display = "";
30268     },
30269     
30270     /**
30271      * Hides this item.
30272      */
30273     hide: function(){
30274         this.hidden = true;
30275         this.td.style.display = "none";
30276     },
30277     
30278     /**
30279      * Convenience function for boolean show/hide.
30280      * @param {Boolean} visible true to show/false to hide
30281      */
30282     setVisible: function(visible){
30283         if(visible) {
30284             this.show();
30285         }else{
30286             this.hide();
30287         }
30288     },
30289     
30290     /**
30291      * Try to focus this item.
30292      */
30293     focus : function(){
30294         Roo.fly(this.el).focus();
30295     },
30296     
30297     /**
30298      * Disables this item.
30299      */
30300     disable : function(){
30301         Roo.fly(this.td).addClass("x-item-disabled");
30302         this.disabled = true;
30303         this.el.disabled = true;
30304     },
30305     
30306     /**
30307      * Enables this item.
30308      */
30309     enable : function(){
30310         Roo.fly(this.td).removeClass("x-item-disabled");
30311         this.disabled = false;
30312         this.el.disabled = false;
30313     }
30314 });
30315
30316
30317 /**
30318  * @class Roo.Toolbar.Separator
30319  * @extends Roo.Toolbar.Item
30320  * A simple toolbar separator class
30321  * @constructor
30322  * Creates a new Separator
30323  */
30324 Roo.Toolbar.Separator = function(cfg){
30325     
30326     var s = document.createElement("span");
30327     s.className = "ytb-sep";
30328     if (cfg) {
30329         cfg.el = s;
30330     }
30331     
30332     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30333 };
30334 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30335     enable:Roo.emptyFn,
30336     disable:Roo.emptyFn,
30337     focus:Roo.emptyFn
30338 });
30339
30340 /**
30341  * @class Roo.Toolbar.Spacer
30342  * @extends Roo.Toolbar.Item
30343  * A simple element that adds extra horizontal space to a toolbar.
30344  * @constructor
30345  * Creates a new Spacer
30346  */
30347 Roo.Toolbar.Spacer = function(cfg){
30348     var s = document.createElement("div");
30349     s.className = "ytb-spacer";
30350     if (cfg) {
30351         cfg.el = s;
30352     }
30353     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30354 };
30355 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30356     enable:Roo.emptyFn,
30357     disable:Roo.emptyFn,
30358     focus:Roo.emptyFn
30359 });
30360
30361 /**
30362  * @class Roo.Toolbar.Fill
30363  * @extends Roo.Toolbar.Spacer
30364  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30365  * @constructor
30366  * Creates a new Spacer
30367  */
30368 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30369     // private
30370     render : function(td){
30371         td.style.width = '100%';
30372         Roo.Toolbar.Fill.superclass.render.call(this, td);
30373     }
30374 });
30375
30376 /**
30377  * @class Roo.Toolbar.TextItem
30378  * @extends Roo.Toolbar.Item
30379  * A simple class that renders text directly into a toolbar.
30380  * @constructor
30381  * Creates a new TextItem
30382  * @param {String} text
30383  */
30384 Roo.Toolbar.TextItem = function(cfg){
30385     var  text = cfg || "";
30386     if (typeof(cfg) == 'object') {
30387         text = cfg.text || "";
30388     }  else {
30389         cfg = null;
30390     }
30391     var s = document.createElement("span");
30392     s.className = "ytb-text";
30393     s.innerHTML = text;
30394     if (cfg) {
30395         cfg.el  = s;
30396     }
30397     
30398     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30399 };
30400 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30401     
30402      
30403     enable:Roo.emptyFn,
30404     disable:Roo.emptyFn,
30405     focus:Roo.emptyFn
30406 });
30407
30408 /**
30409  * @class Roo.Toolbar.Button
30410  * @extends Roo.Button
30411  * A button that renders into a toolbar.
30412  * @constructor
30413  * Creates a new Button
30414  * @param {Object} config A standard {@link Roo.Button} config object
30415  */
30416 Roo.Toolbar.Button = function(config){
30417     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30418 };
30419 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30420     render : function(td){
30421         this.td = td;
30422         Roo.Toolbar.Button.superclass.render.call(this, td);
30423     },
30424     
30425     /**
30426      * Removes and destroys this button
30427      */
30428     destroy : function(){
30429         Roo.Toolbar.Button.superclass.destroy.call(this);
30430         this.td.parentNode.removeChild(this.td);
30431     },
30432     
30433     /**
30434      * Shows this button
30435      */
30436     show: function(){
30437         this.hidden = false;
30438         this.td.style.display = "";
30439     },
30440     
30441     /**
30442      * Hides this button
30443      */
30444     hide: function(){
30445         this.hidden = true;
30446         this.td.style.display = "none";
30447     },
30448
30449     /**
30450      * Disables this item
30451      */
30452     disable : function(){
30453         Roo.fly(this.td).addClass("x-item-disabled");
30454         this.disabled = true;
30455     },
30456
30457     /**
30458      * Enables this item
30459      */
30460     enable : function(){
30461         Roo.fly(this.td).removeClass("x-item-disabled");
30462         this.disabled = false;
30463     }
30464 });
30465 // backwards compat
30466 Roo.ToolbarButton = Roo.Toolbar.Button;
30467
30468 /**
30469  * @class Roo.Toolbar.SplitButton
30470  * @extends Roo.SplitButton
30471  * A menu button that renders into a toolbar.
30472  * @constructor
30473  * Creates a new SplitButton
30474  * @param {Object} config A standard {@link Roo.SplitButton} config object
30475  */
30476 Roo.Toolbar.SplitButton = function(config){
30477     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30478 };
30479 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30480     render : function(td){
30481         this.td = td;
30482         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30483     },
30484     
30485     /**
30486      * Removes and destroys this button
30487      */
30488     destroy : function(){
30489         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30490         this.td.parentNode.removeChild(this.td);
30491     },
30492     
30493     /**
30494      * Shows this button
30495      */
30496     show: function(){
30497         this.hidden = false;
30498         this.td.style.display = "";
30499     },
30500     
30501     /**
30502      * Hides this button
30503      */
30504     hide: function(){
30505         this.hidden = true;
30506         this.td.style.display = "none";
30507     }
30508 });
30509
30510 // backwards compat
30511 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30512  * Based on:
30513  * Ext JS Library 1.1.1
30514  * Copyright(c) 2006-2007, Ext JS, LLC.
30515  *
30516  * Originally Released Under LGPL - original licence link has changed is not relivant.
30517  *
30518  * Fork - LGPL
30519  * <script type="text/javascript">
30520  */
30521  
30522 /**
30523  * @class Roo.PagingToolbar
30524  * @extends Roo.Toolbar
30525  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30526  * @constructor
30527  * Create a new PagingToolbar
30528  * @param {Object} config The config object
30529  */
30530 Roo.PagingToolbar = function(el, ds, config)
30531 {
30532     // old args format still supported... - xtype is prefered..
30533     if (typeof(el) == 'object' && el.xtype) {
30534         // created from xtype...
30535         config = el;
30536         ds = el.dataSource;
30537         el = config.container;
30538     }
30539     var items = [];
30540     if (config.items) {
30541         items = config.items;
30542         config.items = [];
30543     }
30544     
30545     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30546     this.ds = ds;
30547     this.cursor = 0;
30548     this.renderButtons(this.el);
30549     this.bind(ds);
30550     
30551     // supprot items array.
30552    
30553     Roo.each(items, function(e) {
30554         this.add(Roo.factory(e));
30555     },this);
30556     
30557 };
30558
30559 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30560     /**
30561      * @cfg {Roo.data.Store} dataSource
30562      * The underlying data store providing the paged data
30563      */
30564     /**
30565      * @cfg {String/HTMLElement/Element} container
30566      * container The id or element that will contain the toolbar
30567      */
30568     /**
30569      * @cfg {Boolean} displayInfo
30570      * True to display the displayMsg (defaults to false)
30571      */
30572     /**
30573      * @cfg {Number} pageSize
30574      * The number of records to display per page (defaults to 20)
30575      */
30576     pageSize: 20,
30577     /**
30578      * @cfg {String} displayMsg
30579      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30580      */
30581     displayMsg : 'Displaying {0} - {1} of {2}',
30582     /**
30583      * @cfg {String} emptyMsg
30584      * The message to display when no records are found (defaults to "No data to display")
30585      */
30586     emptyMsg : 'No data to display',
30587     /**
30588      * Customizable piece of the default paging text (defaults to "Page")
30589      * @type String
30590      */
30591     beforePageText : "Page",
30592     /**
30593      * Customizable piece of the default paging text (defaults to "of %0")
30594      * @type String
30595      */
30596     afterPageText : "of {0}",
30597     /**
30598      * Customizable piece of the default paging text (defaults to "First Page")
30599      * @type String
30600      */
30601     firstText : "First Page",
30602     /**
30603      * Customizable piece of the default paging text (defaults to "Previous Page")
30604      * @type String
30605      */
30606     prevText : "Previous Page",
30607     /**
30608      * Customizable piece of the default paging text (defaults to "Next Page")
30609      * @type String
30610      */
30611     nextText : "Next Page",
30612     /**
30613      * Customizable piece of the default paging text (defaults to "Last Page")
30614      * @type String
30615      */
30616     lastText : "Last Page",
30617     /**
30618      * Customizable piece of the default paging text (defaults to "Refresh")
30619      * @type String
30620      */
30621     refreshText : "Refresh",
30622
30623     // private
30624     renderButtons : function(el){
30625         Roo.PagingToolbar.superclass.render.call(this, el);
30626         this.first = this.addButton({
30627             tooltip: this.firstText,
30628             cls: "x-btn-icon x-grid-page-first",
30629             disabled: true,
30630             handler: this.onClick.createDelegate(this, ["first"])
30631         });
30632         this.prev = this.addButton({
30633             tooltip: this.prevText,
30634             cls: "x-btn-icon x-grid-page-prev",
30635             disabled: true,
30636             handler: this.onClick.createDelegate(this, ["prev"])
30637         });
30638         //this.addSeparator();
30639         this.add(this.beforePageText);
30640         this.field = Roo.get(this.addDom({
30641            tag: "input",
30642            type: "text",
30643            size: "3",
30644            value: "1",
30645            cls: "x-grid-page-number"
30646         }).el);
30647         this.field.on("keydown", this.onPagingKeydown, this);
30648         this.field.on("focus", function(){this.dom.select();});
30649         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30650         this.field.setHeight(18);
30651         //this.addSeparator();
30652         this.next = this.addButton({
30653             tooltip: this.nextText,
30654             cls: "x-btn-icon x-grid-page-next",
30655             disabled: true,
30656             handler: this.onClick.createDelegate(this, ["next"])
30657         });
30658         this.last = this.addButton({
30659             tooltip: this.lastText,
30660             cls: "x-btn-icon x-grid-page-last",
30661             disabled: true,
30662             handler: this.onClick.createDelegate(this, ["last"])
30663         });
30664         //this.addSeparator();
30665         this.loading = this.addButton({
30666             tooltip: this.refreshText,
30667             cls: "x-btn-icon x-grid-loading",
30668             handler: this.onClick.createDelegate(this, ["refresh"])
30669         });
30670
30671         if(this.displayInfo){
30672             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30673         }
30674     },
30675
30676     // private
30677     updateInfo : function(){
30678         if(this.displayEl){
30679             var count = this.ds.getCount();
30680             var msg = count == 0 ?
30681                 this.emptyMsg :
30682                 String.format(
30683                     this.displayMsg,
30684                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30685                 );
30686             this.displayEl.update(msg);
30687         }
30688     },
30689
30690     // private
30691     onLoad : function(ds, r, o){
30692        this.cursor = o.params ? o.params.start : 0;
30693        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30694
30695        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30696        this.field.dom.value = ap;
30697        this.first.setDisabled(ap == 1);
30698        this.prev.setDisabled(ap == 1);
30699        this.next.setDisabled(ap == ps);
30700        this.last.setDisabled(ap == ps);
30701        this.loading.enable();
30702        this.updateInfo();
30703     },
30704
30705     // private
30706     getPageData : function(){
30707         var total = this.ds.getTotalCount();
30708         return {
30709             total : total,
30710             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30711             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30712         };
30713     },
30714
30715     // private
30716     onLoadError : function(){
30717         this.loading.enable();
30718     },
30719
30720     // private
30721     onPagingKeydown : function(e){
30722         var k = e.getKey();
30723         var d = this.getPageData();
30724         if(k == e.RETURN){
30725             var v = this.field.dom.value, pageNum;
30726             if(!v || isNaN(pageNum = parseInt(v, 10))){
30727                 this.field.dom.value = d.activePage;
30728                 return;
30729             }
30730             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30731             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30732             e.stopEvent();
30733         }
30734         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))
30735         {
30736           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30737           this.field.dom.value = pageNum;
30738           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30739           e.stopEvent();
30740         }
30741         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30742         {
30743           var v = this.field.dom.value, pageNum; 
30744           var increment = (e.shiftKey) ? 10 : 1;
30745           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30746             increment *= -1;
30747           }
30748           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30749             this.field.dom.value = d.activePage;
30750             return;
30751           }
30752           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30753           {
30754             this.field.dom.value = parseInt(v, 10) + increment;
30755             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30756             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30757           }
30758           e.stopEvent();
30759         }
30760     },
30761
30762     // private
30763     beforeLoad : function(){
30764         if(this.loading){
30765             this.loading.disable();
30766         }
30767     },
30768
30769     // private
30770     onClick : function(which){
30771         var ds = this.ds;
30772         switch(which){
30773             case "first":
30774                 ds.load({params:{start: 0, limit: this.pageSize}});
30775             break;
30776             case "prev":
30777                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30778             break;
30779             case "next":
30780                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30781             break;
30782             case "last":
30783                 var total = ds.getTotalCount();
30784                 var extra = total % this.pageSize;
30785                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30786                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30787             break;
30788             case "refresh":
30789                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30790             break;
30791         }
30792     },
30793
30794     /**
30795      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30796      * @param {Roo.data.Store} store The data store to unbind
30797      */
30798     unbind : function(ds){
30799         ds.un("beforeload", this.beforeLoad, this);
30800         ds.un("load", this.onLoad, this);
30801         ds.un("loadexception", this.onLoadError, this);
30802         ds.un("remove", this.updateInfo, this);
30803         ds.un("add", this.updateInfo, this);
30804         this.ds = undefined;
30805     },
30806
30807     /**
30808      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30809      * @param {Roo.data.Store} store The data store to bind
30810      */
30811     bind : function(ds){
30812         ds.on("beforeload", this.beforeLoad, this);
30813         ds.on("load", this.onLoad, this);
30814         ds.on("loadexception", this.onLoadError, this);
30815         ds.on("remove", this.updateInfo, this);
30816         ds.on("add", this.updateInfo, this);
30817         this.ds = ds;
30818     }
30819 });/*
30820  * Based on:
30821  * Ext JS Library 1.1.1
30822  * Copyright(c) 2006-2007, Ext JS, LLC.
30823  *
30824  * Originally Released Under LGPL - original licence link has changed is not relivant.
30825  *
30826  * Fork - LGPL
30827  * <script type="text/javascript">
30828  */
30829
30830 /**
30831  * @class Roo.Resizable
30832  * @extends Roo.util.Observable
30833  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30834  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30835  * 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
30836  * the element will be wrapped for you automatically.</p>
30837  * <p>Here is the list of valid resize handles:</p>
30838  * <pre>
30839 Value   Description
30840 ------  -------------------
30841  'n'     north
30842  's'     south
30843  'e'     east
30844  'w'     west
30845  'nw'    northwest
30846  'sw'    southwest
30847  'se'    southeast
30848  'ne'    northeast
30849  'hd'    horizontal drag
30850  'all'   all
30851 </pre>
30852  * <p>Here's an example showing the creation of a typical Resizable:</p>
30853  * <pre><code>
30854 var resizer = new Roo.Resizable("element-id", {
30855     handles: 'all',
30856     minWidth: 200,
30857     minHeight: 100,
30858     maxWidth: 500,
30859     maxHeight: 400,
30860     pinned: true
30861 });
30862 resizer.on("resize", myHandler);
30863 </code></pre>
30864  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30865  * resizer.east.setDisplayed(false);</p>
30866  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30867  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30868  * resize operation's new size (defaults to [0, 0])
30869  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30870  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30871  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30872  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30873  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30874  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30875  * @cfg {Number} width The width of the element in pixels (defaults to null)
30876  * @cfg {Number} height The height of the element in pixels (defaults to null)
30877  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30878  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30879  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30880  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30881  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30882  * in favor of the handles config option (defaults to false)
30883  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30884  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30885  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30886  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30887  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30888  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30889  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30890  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30891  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30892  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30893  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30894  * @constructor
30895  * Create a new resizable component
30896  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30897  * @param {Object} config configuration options
30898   */
30899 Roo.Resizable = function(el, config)
30900 {
30901     this.el = Roo.get(el);
30902
30903     if(config && config.wrap){
30904         config.resizeChild = this.el;
30905         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30906         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30907         this.el.setStyle("overflow", "hidden");
30908         this.el.setPositioning(config.resizeChild.getPositioning());
30909         config.resizeChild.clearPositioning();
30910         if(!config.width || !config.height){
30911             var csize = config.resizeChild.getSize();
30912             this.el.setSize(csize.width, csize.height);
30913         }
30914         if(config.pinned && !config.adjustments){
30915             config.adjustments = "auto";
30916         }
30917     }
30918
30919     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30920     this.proxy.unselectable();
30921     this.proxy.enableDisplayMode('block');
30922
30923     Roo.apply(this, config);
30924
30925     if(this.pinned){
30926         this.disableTrackOver = true;
30927         this.el.addClass("x-resizable-pinned");
30928     }
30929     // if the element isn't positioned, make it relative
30930     var position = this.el.getStyle("position");
30931     if(position != "absolute" && position != "fixed"){
30932         this.el.setStyle("position", "relative");
30933     }
30934     if(!this.handles){ // no handles passed, must be legacy style
30935         this.handles = 's,e,se';
30936         if(this.multiDirectional){
30937             this.handles += ',n,w';
30938         }
30939     }
30940     if(this.handles == "all"){
30941         this.handles = "n s e w ne nw se sw";
30942     }
30943     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30944     var ps = Roo.Resizable.positions;
30945     for(var i = 0, len = hs.length; i < len; i++){
30946         if(hs[i] && ps[hs[i]]){
30947             var pos = ps[hs[i]];
30948             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30949         }
30950     }
30951     // legacy
30952     this.corner = this.southeast;
30953     
30954     // updateBox = the box can move..
30955     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30956         this.updateBox = true;
30957     }
30958
30959     this.activeHandle = null;
30960
30961     if(this.resizeChild){
30962         if(typeof this.resizeChild == "boolean"){
30963             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30964         }else{
30965             this.resizeChild = Roo.get(this.resizeChild, true);
30966         }
30967     }
30968     
30969     if(this.adjustments == "auto"){
30970         var rc = this.resizeChild;
30971         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30972         if(rc && (hw || hn)){
30973             rc.position("relative");
30974             rc.setLeft(hw ? hw.el.getWidth() : 0);
30975             rc.setTop(hn ? hn.el.getHeight() : 0);
30976         }
30977         this.adjustments = [
30978             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30979             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30980         ];
30981     }
30982
30983     if(this.draggable){
30984         this.dd = this.dynamic ?
30985             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30986         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30987     }
30988
30989     // public events
30990     this.addEvents({
30991         /**
30992          * @event beforeresize
30993          * Fired before resize is allowed. Set enabled to false to cancel resize.
30994          * @param {Roo.Resizable} this
30995          * @param {Roo.EventObject} e The mousedown event
30996          */
30997         "beforeresize" : true,
30998         /**
30999          * @event resizing
31000          * Fired a resizing.
31001          * @param {Roo.Resizable} this
31002          * @param {Number} x The new x position
31003          * @param {Number} y The new y position
31004          * @param {Number} w The new w width
31005          * @param {Number} h The new h hight
31006          * @param {Roo.EventObject} e The mouseup event
31007          */
31008         "resizing" : true,
31009         /**
31010          * @event resize
31011          * Fired after a resize.
31012          * @param {Roo.Resizable} this
31013          * @param {Number} width The new width
31014          * @param {Number} height The new height
31015          * @param {Roo.EventObject} e The mouseup event
31016          */
31017         "resize" : true
31018     });
31019
31020     if(this.width !== null && this.height !== null){
31021         this.resizeTo(this.width, this.height);
31022     }else{
31023         this.updateChildSize();
31024     }
31025     if(Roo.isIE){
31026         this.el.dom.style.zoom = 1;
31027     }
31028     Roo.Resizable.superclass.constructor.call(this);
31029 };
31030
31031 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31032         resizeChild : false,
31033         adjustments : [0, 0],
31034         minWidth : 5,
31035         minHeight : 5,
31036         maxWidth : 10000,
31037         maxHeight : 10000,
31038         enabled : true,
31039         animate : false,
31040         duration : .35,
31041         dynamic : false,
31042         handles : false,
31043         multiDirectional : false,
31044         disableTrackOver : false,
31045         easing : 'easeOutStrong',
31046         widthIncrement : 0,
31047         heightIncrement : 0,
31048         pinned : false,
31049         width : null,
31050         height : null,
31051         preserveRatio : false,
31052         transparent: false,
31053         minX: 0,
31054         minY: 0,
31055         draggable: false,
31056
31057         /**
31058          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31059          */
31060         constrainTo: undefined,
31061         /**
31062          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31063          */
31064         resizeRegion: undefined,
31065
31066
31067     /**
31068      * Perform a manual resize
31069      * @param {Number} width
31070      * @param {Number} height
31071      */
31072     resizeTo : function(width, height){
31073         this.el.setSize(width, height);
31074         this.updateChildSize();
31075         this.fireEvent("resize", this, width, height, null);
31076     },
31077
31078     // private
31079     startSizing : function(e, handle){
31080         this.fireEvent("beforeresize", this, e);
31081         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31082
31083             if(!this.overlay){
31084                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31085                 this.overlay.unselectable();
31086                 this.overlay.enableDisplayMode("block");
31087                 this.overlay.on("mousemove", this.onMouseMove, this);
31088                 this.overlay.on("mouseup", this.onMouseUp, this);
31089             }
31090             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31091
31092             this.resizing = true;
31093             this.startBox = this.el.getBox();
31094             this.startPoint = e.getXY();
31095             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31096                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31097
31098             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31099             this.overlay.show();
31100
31101             if(this.constrainTo) {
31102                 var ct = Roo.get(this.constrainTo);
31103                 this.resizeRegion = ct.getRegion().adjust(
31104                     ct.getFrameWidth('t'),
31105                     ct.getFrameWidth('l'),
31106                     -ct.getFrameWidth('b'),
31107                     -ct.getFrameWidth('r')
31108                 );
31109             }
31110
31111             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31112             this.proxy.show();
31113             this.proxy.setBox(this.startBox);
31114             if(!this.dynamic){
31115                 this.proxy.setStyle('visibility', 'visible');
31116             }
31117         }
31118     },
31119
31120     // private
31121     onMouseDown : function(handle, e){
31122         if(this.enabled){
31123             e.stopEvent();
31124             this.activeHandle = handle;
31125             this.startSizing(e, handle);
31126         }
31127     },
31128
31129     // private
31130     onMouseUp : function(e){
31131         var size = this.resizeElement();
31132         this.resizing = false;
31133         this.handleOut();
31134         this.overlay.hide();
31135         this.proxy.hide();
31136         this.fireEvent("resize", this, size.width, size.height, e);
31137     },
31138
31139     // private
31140     updateChildSize : function(){
31141         
31142         if(this.resizeChild){
31143             var el = this.el;
31144             var child = this.resizeChild;
31145             var adj = this.adjustments;
31146             if(el.dom.offsetWidth){
31147                 var b = el.getSize(true);
31148                 child.setSize(b.width+adj[0], b.height+adj[1]);
31149             }
31150             // Second call here for IE
31151             // The first call enables instant resizing and
31152             // the second call corrects scroll bars if they
31153             // exist
31154             if(Roo.isIE){
31155                 setTimeout(function(){
31156                     if(el.dom.offsetWidth){
31157                         var b = el.getSize(true);
31158                         child.setSize(b.width+adj[0], b.height+adj[1]);
31159                     }
31160                 }, 10);
31161             }
31162         }
31163     },
31164
31165     // private
31166     snap : function(value, inc, min){
31167         if(!inc || !value) {
31168             return value;
31169         }
31170         var newValue = value;
31171         var m = value % inc;
31172         if(m > 0){
31173             if(m > (inc/2)){
31174                 newValue = value + (inc-m);
31175             }else{
31176                 newValue = value - m;
31177             }
31178         }
31179         return Math.max(min, newValue);
31180     },
31181
31182     // private
31183     resizeElement : function(){
31184         var box = this.proxy.getBox();
31185         if(this.updateBox){
31186             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31187         }else{
31188             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31189         }
31190         this.updateChildSize();
31191         if(!this.dynamic){
31192             this.proxy.hide();
31193         }
31194         return box;
31195     },
31196
31197     // private
31198     constrain : function(v, diff, m, mx){
31199         if(v - diff < m){
31200             diff = v - m;
31201         }else if(v - diff > mx){
31202             diff = mx - v;
31203         }
31204         return diff;
31205     },
31206
31207     // private
31208     onMouseMove : function(e){
31209         
31210         if(this.enabled){
31211             try{// try catch so if something goes wrong the user doesn't get hung
31212
31213             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31214                 return;
31215             }
31216
31217             //var curXY = this.startPoint;
31218             var curSize = this.curSize || this.startBox;
31219             var x = this.startBox.x, y = this.startBox.y;
31220             var ox = x, oy = y;
31221             var w = curSize.width, h = curSize.height;
31222             var ow = w, oh = h;
31223             var mw = this.minWidth, mh = this.minHeight;
31224             var mxw = this.maxWidth, mxh = this.maxHeight;
31225             var wi = this.widthIncrement;
31226             var hi = this.heightIncrement;
31227
31228             var eventXY = e.getXY();
31229             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31230             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31231
31232             var pos = this.activeHandle.position;
31233
31234             switch(pos){
31235                 case "east":
31236                     w += diffX;
31237                     w = Math.min(Math.max(mw, w), mxw);
31238                     break;
31239              
31240                 case "south":
31241                     h += diffY;
31242                     h = Math.min(Math.max(mh, h), mxh);
31243                     break;
31244                 case "southeast":
31245                     w += diffX;
31246                     h += diffY;
31247                     w = Math.min(Math.max(mw, w), mxw);
31248                     h = Math.min(Math.max(mh, h), mxh);
31249                     break;
31250                 case "north":
31251                     diffY = this.constrain(h, diffY, mh, mxh);
31252                     y += diffY;
31253                     h -= diffY;
31254                     break;
31255                 case "hdrag":
31256                     
31257                     if (wi) {
31258                         var adiffX = Math.abs(diffX);
31259                         var sub = (adiffX % wi); // how much 
31260                         if (sub > (wi/2)) { // far enough to snap
31261                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31262                         } else {
31263                             // remove difference.. 
31264                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31265                         }
31266                     }
31267                     x += diffX;
31268                     x = Math.max(this.minX, x);
31269                     break;
31270                 case "west":
31271                     diffX = this.constrain(w, diffX, mw, mxw);
31272                     x += diffX;
31273                     w -= diffX;
31274                     break;
31275                 case "northeast":
31276                     w += diffX;
31277                     w = Math.min(Math.max(mw, w), mxw);
31278                     diffY = this.constrain(h, diffY, mh, mxh);
31279                     y += diffY;
31280                     h -= diffY;
31281                     break;
31282                 case "northwest":
31283                     diffX = this.constrain(w, diffX, mw, mxw);
31284                     diffY = this.constrain(h, diffY, mh, mxh);
31285                     y += diffY;
31286                     h -= diffY;
31287                     x += diffX;
31288                     w -= diffX;
31289                     break;
31290                case "southwest":
31291                     diffX = this.constrain(w, diffX, mw, mxw);
31292                     h += diffY;
31293                     h = Math.min(Math.max(mh, h), mxh);
31294                     x += diffX;
31295                     w -= diffX;
31296                     break;
31297             }
31298
31299             var sw = this.snap(w, wi, mw);
31300             var sh = this.snap(h, hi, mh);
31301             if(sw != w || sh != h){
31302                 switch(pos){
31303                     case "northeast":
31304                         y -= sh - h;
31305                     break;
31306                     case "north":
31307                         y -= sh - h;
31308                         break;
31309                     case "southwest":
31310                         x -= sw - w;
31311                     break;
31312                     case "west":
31313                         x -= sw - w;
31314                         break;
31315                     case "northwest":
31316                         x -= sw - w;
31317                         y -= sh - h;
31318                     break;
31319                 }
31320                 w = sw;
31321                 h = sh;
31322             }
31323
31324             if(this.preserveRatio){
31325                 switch(pos){
31326                     case "southeast":
31327                     case "east":
31328                         h = oh * (w/ow);
31329                         h = Math.min(Math.max(mh, h), mxh);
31330                         w = ow * (h/oh);
31331                        break;
31332                     case "south":
31333                         w = ow * (h/oh);
31334                         w = Math.min(Math.max(mw, w), mxw);
31335                         h = oh * (w/ow);
31336                         break;
31337                     case "northeast":
31338                         w = ow * (h/oh);
31339                         w = Math.min(Math.max(mw, w), mxw);
31340                         h = oh * (w/ow);
31341                     break;
31342                     case "north":
31343                         var tw = w;
31344                         w = ow * (h/oh);
31345                         w = Math.min(Math.max(mw, w), mxw);
31346                         h = oh * (w/ow);
31347                         x += (tw - w) / 2;
31348                         break;
31349                     case "southwest":
31350                         h = oh * (w/ow);
31351                         h = Math.min(Math.max(mh, h), mxh);
31352                         var tw = w;
31353                         w = ow * (h/oh);
31354                         x += tw - w;
31355                         break;
31356                     case "west":
31357                         var th = h;
31358                         h = oh * (w/ow);
31359                         h = Math.min(Math.max(mh, h), mxh);
31360                         y += (th - h) / 2;
31361                         var tw = w;
31362                         w = ow * (h/oh);
31363                         x += tw - w;
31364                        break;
31365                     case "northwest":
31366                         var tw = w;
31367                         var th = h;
31368                         h = oh * (w/ow);
31369                         h = Math.min(Math.max(mh, h), mxh);
31370                         w = ow * (h/oh);
31371                         y += th - h;
31372                         x += tw - w;
31373                        break;
31374
31375                 }
31376             }
31377             if (pos == 'hdrag') {
31378                 w = ow;
31379             }
31380             this.proxy.setBounds(x, y, w, h);
31381             if(this.dynamic){
31382                 this.resizeElement();
31383             }
31384             }catch(e){}
31385         }
31386         this.fireEvent("resizing", this, x, y, w, h, e);
31387     },
31388
31389     // private
31390     handleOver : function(){
31391         if(this.enabled){
31392             this.el.addClass("x-resizable-over");
31393         }
31394     },
31395
31396     // private
31397     handleOut : function(){
31398         if(!this.resizing){
31399             this.el.removeClass("x-resizable-over");
31400         }
31401     },
31402
31403     /**
31404      * Returns the element this component is bound to.
31405      * @return {Roo.Element}
31406      */
31407     getEl : function(){
31408         return this.el;
31409     },
31410
31411     /**
31412      * Returns the resizeChild element (or null).
31413      * @return {Roo.Element}
31414      */
31415     getResizeChild : function(){
31416         return this.resizeChild;
31417     },
31418     groupHandler : function()
31419     {
31420         
31421     },
31422     /**
31423      * Destroys this resizable. If the element was wrapped and
31424      * removeEl is not true then the element remains.
31425      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31426      */
31427     destroy : function(removeEl){
31428         this.proxy.remove();
31429         if(this.overlay){
31430             this.overlay.removeAllListeners();
31431             this.overlay.remove();
31432         }
31433         var ps = Roo.Resizable.positions;
31434         for(var k in ps){
31435             if(typeof ps[k] != "function" && this[ps[k]]){
31436                 var h = this[ps[k]];
31437                 h.el.removeAllListeners();
31438                 h.el.remove();
31439             }
31440         }
31441         if(removeEl){
31442             this.el.update("");
31443             this.el.remove();
31444         }
31445     }
31446 });
31447
31448 // private
31449 // hash to map config positions to true positions
31450 Roo.Resizable.positions = {
31451     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31452     hd: "hdrag"
31453 };
31454
31455 // private
31456 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31457     if(!this.tpl){
31458         // only initialize the template if resizable is used
31459         var tpl = Roo.DomHelper.createTemplate(
31460             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31461         );
31462         tpl.compile();
31463         Roo.Resizable.Handle.prototype.tpl = tpl;
31464     }
31465     this.position = pos;
31466     this.rz = rz;
31467     // show north drag fro topdra
31468     var handlepos = pos == 'hdrag' ? 'north' : pos;
31469     
31470     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31471     if (pos == 'hdrag') {
31472         this.el.setStyle('cursor', 'pointer');
31473     }
31474     this.el.unselectable();
31475     if(transparent){
31476         this.el.setOpacity(0);
31477     }
31478     this.el.on("mousedown", this.onMouseDown, this);
31479     if(!disableTrackOver){
31480         this.el.on("mouseover", this.onMouseOver, this);
31481         this.el.on("mouseout", this.onMouseOut, this);
31482     }
31483 };
31484
31485 // private
31486 Roo.Resizable.Handle.prototype = {
31487     afterResize : function(rz){
31488         Roo.log('after?');
31489         // do nothing
31490     },
31491     // private
31492     onMouseDown : function(e){
31493         this.rz.onMouseDown(this, e);
31494     },
31495     // private
31496     onMouseOver : function(e){
31497         this.rz.handleOver(this, e);
31498     },
31499     // private
31500     onMouseOut : function(e){
31501         this.rz.handleOut(this, e);
31502     }
31503 };/*
31504  * Based on:
31505  * Ext JS Library 1.1.1
31506  * Copyright(c) 2006-2007, Ext JS, LLC.
31507  *
31508  * Originally Released Under LGPL - original licence link has changed is not relivant.
31509  *
31510  * Fork - LGPL
31511  * <script type="text/javascript">
31512  */
31513
31514 /**
31515  * @class Roo.Editor
31516  * @extends Roo.Component
31517  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31518  * @constructor
31519  * Create a new Editor
31520  * @param {Roo.form.Field} field The Field object (or descendant)
31521  * @param {Object} config The config object
31522  */
31523 Roo.Editor = function(field, config){
31524     Roo.Editor.superclass.constructor.call(this, config);
31525     this.field = field;
31526     this.addEvents({
31527         /**
31528              * @event beforestartedit
31529              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31530              * false from the handler of this event.
31531              * @param {Editor} this
31532              * @param {Roo.Element} boundEl The underlying element bound to this editor
31533              * @param {Mixed} value The field value being set
31534              */
31535         "beforestartedit" : true,
31536         /**
31537              * @event startedit
31538              * Fires when this editor is displayed
31539              * @param {Roo.Element} boundEl The underlying element bound to this editor
31540              * @param {Mixed} value The starting field value
31541              */
31542         "startedit" : true,
31543         /**
31544              * @event beforecomplete
31545              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31546              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31547              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31548              * event will not fire since no edit actually occurred.
31549              * @param {Editor} this
31550              * @param {Mixed} value The current field value
31551              * @param {Mixed} startValue The original field value
31552              */
31553         "beforecomplete" : true,
31554         /**
31555              * @event complete
31556              * Fires after editing is complete and any changed value has been written to the underlying field.
31557              * @param {Editor} this
31558              * @param {Mixed} value The current field value
31559              * @param {Mixed} startValue The original field value
31560              */
31561         "complete" : true,
31562         /**
31563          * @event specialkey
31564          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31565          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31566          * @param {Roo.form.Field} this
31567          * @param {Roo.EventObject} e The event object
31568          */
31569         "specialkey" : true
31570     });
31571 };
31572
31573 Roo.extend(Roo.Editor, Roo.Component, {
31574     /**
31575      * @cfg {Boolean/String} autosize
31576      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31577      * or "height" to adopt the height only (defaults to false)
31578      */
31579     /**
31580      * @cfg {Boolean} revertInvalid
31581      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31582      * validation fails (defaults to true)
31583      */
31584     /**
31585      * @cfg {Boolean} ignoreNoChange
31586      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31587      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31588      * will never be ignored.
31589      */
31590     /**
31591      * @cfg {Boolean} hideEl
31592      * False to keep the bound element visible while the editor is displayed (defaults to true)
31593      */
31594     /**
31595      * @cfg {Mixed} value
31596      * The data value of the underlying field (defaults to "")
31597      */
31598     value : "",
31599     /**
31600      * @cfg {String} alignment
31601      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31602      */
31603     alignment: "c-c?",
31604     /**
31605      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31606      * for bottom-right shadow (defaults to "frame")
31607      */
31608     shadow : "frame",
31609     /**
31610      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31611      */
31612     constrain : false,
31613     /**
31614      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31615      */
31616     completeOnEnter : false,
31617     /**
31618      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31619      */
31620     cancelOnEsc : false,
31621     /**
31622      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31623      */
31624     updateEl : false,
31625
31626     // private
31627     onRender : function(ct, position){
31628         this.el = new Roo.Layer({
31629             shadow: this.shadow,
31630             cls: "x-editor",
31631             parentEl : ct,
31632             shim : this.shim,
31633             shadowOffset:4,
31634             id: this.id,
31635             constrain: this.constrain
31636         });
31637         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31638         if(this.field.msgTarget != 'title'){
31639             this.field.msgTarget = 'qtip';
31640         }
31641         this.field.render(this.el);
31642         if(Roo.isGecko){
31643             this.field.el.dom.setAttribute('autocomplete', 'off');
31644         }
31645         this.field.on("specialkey", this.onSpecialKey, this);
31646         if(this.swallowKeys){
31647             this.field.el.swallowEvent(['keydown','keypress']);
31648         }
31649         this.field.show();
31650         this.field.on("blur", this.onBlur, this);
31651         if(this.field.grow){
31652             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31653         }
31654     },
31655
31656     onSpecialKey : function(field, e)
31657     {
31658         //Roo.log('editor onSpecialKey');
31659         if(this.completeOnEnter && e.getKey() == e.ENTER){
31660             e.stopEvent();
31661             this.completeEdit();
31662             return;
31663         }
31664         // do not fire special key otherwise it might hide close the editor...
31665         if(e.getKey() == e.ENTER){    
31666             return;
31667         }
31668         if(this.cancelOnEsc && e.getKey() == e.ESC){
31669             this.cancelEdit();
31670             return;
31671         } 
31672         this.fireEvent('specialkey', field, e);
31673     
31674     },
31675
31676     /**
31677      * Starts the editing process and shows the editor.
31678      * @param {String/HTMLElement/Element} el The element to edit
31679      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31680       * to the innerHTML of el.
31681      */
31682     startEdit : function(el, value){
31683         if(this.editing){
31684             this.completeEdit();
31685         }
31686         this.boundEl = Roo.get(el);
31687         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31688         if(!this.rendered){
31689             this.render(this.parentEl || document.body);
31690         }
31691         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31692             return;
31693         }
31694         this.startValue = v;
31695         this.field.setValue(v);
31696         if(this.autoSize){
31697             var sz = this.boundEl.getSize();
31698             switch(this.autoSize){
31699                 case "width":
31700                 this.setSize(sz.width,  "");
31701                 break;
31702                 case "height":
31703                 this.setSize("",  sz.height);
31704                 break;
31705                 default:
31706                 this.setSize(sz.width,  sz.height);
31707             }
31708         }
31709         this.el.alignTo(this.boundEl, this.alignment);
31710         this.editing = true;
31711         if(Roo.QuickTips){
31712             Roo.QuickTips.disable();
31713         }
31714         this.show();
31715     },
31716
31717     /**
31718      * Sets the height and width of this editor.
31719      * @param {Number} width The new width
31720      * @param {Number} height The new height
31721      */
31722     setSize : function(w, h){
31723         this.field.setSize(w, h);
31724         if(this.el){
31725             this.el.sync();
31726         }
31727     },
31728
31729     /**
31730      * Realigns the editor to the bound field based on the current alignment config value.
31731      */
31732     realign : function(){
31733         this.el.alignTo(this.boundEl, this.alignment);
31734     },
31735
31736     /**
31737      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31738      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31739      */
31740     completeEdit : function(remainVisible){
31741         if(!this.editing){
31742             return;
31743         }
31744         var v = this.getValue();
31745         if(this.revertInvalid !== false && !this.field.isValid()){
31746             v = this.startValue;
31747             this.cancelEdit(true);
31748         }
31749         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31750             this.editing = false;
31751             this.hide();
31752             return;
31753         }
31754         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31755             this.editing = false;
31756             if(this.updateEl && this.boundEl){
31757                 this.boundEl.update(v);
31758             }
31759             if(remainVisible !== true){
31760                 this.hide();
31761             }
31762             this.fireEvent("complete", this, v, this.startValue);
31763         }
31764     },
31765
31766     // private
31767     onShow : function(){
31768         this.el.show();
31769         if(this.hideEl !== false){
31770             this.boundEl.hide();
31771         }
31772         this.field.show();
31773         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31774             this.fixIEFocus = true;
31775             this.deferredFocus.defer(50, this);
31776         }else{
31777             this.field.focus();
31778         }
31779         this.fireEvent("startedit", this.boundEl, this.startValue);
31780     },
31781
31782     deferredFocus : function(){
31783         if(this.editing){
31784             this.field.focus();
31785         }
31786     },
31787
31788     /**
31789      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31790      * reverted to the original starting value.
31791      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31792      * cancel (defaults to false)
31793      */
31794     cancelEdit : function(remainVisible){
31795         if(this.editing){
31796             this.setValue(this.startValue);
31797             if(remainVisible !== true){
31798                 this.hide();
31799             }
31800         }
31801     },
31802
31803     // private
31804     onBlur : function(){
31805         if(this.allowBlur !== true && this.editing){
31806             this.completeEdit();
31807         }
31808     },
31809
31810     // private
31811     onHide : function(){
31812         if(this.editing){
31813             this.completeEdit();
31814             return;
31815         }
31816         this.field.blur();
31817         if(this.field.collapse){
31818             this.field.collapse();
31819         }
31820         this.el.hide();
31821         if(this.hideEl !== false){
31822             this.boundEl.show();
31823         }
31824         if(Roo.QuickTips){
31825             Roo.QuickTips.enable();
31826         }
31827     },
31828
31829     /**
31830      * Sets the data value of the editor
31831      * @param {Mixed} value Any valid value supported by the underlying field
31832      */
31833     setValue : function(v){
31834         this.field.setValue(v);
31835     },
31836
31837     /**
31838      * Gets the data value of the editor
31839      * @return {Mixed} The data value
31840      */
31841     getValue : function(){
31842         return this.field.getValue();
31843     }
31844 });/*
31845  * Based on:
31846  * Ext JS Library 1.1.1
31847  * Copyright(c) 2006-2007, Ext JS, LLC.
31848  *
31849  * Originally Released Under LGPL - original licence link has changed is not relivant.
31850  *
31851  * Fork - LGPL
31852  * <script type="text/javascript">
31853  */
31854  
31855 /**
31856  * @class Roo.BasicDialog
31857  * @extends Roo.util.Observable
31858  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31859  * <pre><code>
31860 var dlg = new Roo.BasicDialog("my-dlg", {
31861     height: 200,
31862     width: 300,
31863     minHeight: 100,
31864     minWidth: 150,
31865     modal: true,
31866     proxyDrag: true,
31867     shadow: true
31868 });
31869 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31870 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31871 dlg.addButton('Cancel', dlg.hide, dlg);
31872 dlg.show();
31873 </code></pre>
31874   <b>A Dialog should always be a direct child of the body element.</b>
31875  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31876  * @cfg {String} title Default text to display in the title bar (defaults to null)
31877  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31878  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31879  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31880  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31881  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31882  * (defaults to null with no animation)
31883  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31884  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31885  * property for valid values (defaults to 'all')
31886  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31887  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31888  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31889  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31890  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31891  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31892  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31893  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31894  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31895  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31896  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31897  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31898  * draggable = true (defaults to false)
31899  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31900  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31901  * shadow (defaults to false)
31902  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31903  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31904  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31905  * @cfg {Array} buttons Array of buttons
31906  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31907  * @constructor
31908  * Create a new BasicDialog.
31909  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31910  * @param {Object} config Configuration options
31911  */
31912 Roo.BasicDialog = function(el, config){
31913     this.el = Roo.get(el);
31914     var dh = Roo.DomHelper;
31915     if(!this.el && config && config.autoCreate){
31916         if(typeof config.autoCreate == "object"){
31917             if(!config.autoCreate.id){
31918                 config.autoCreate.id = el;
31919             }
31920             this.el = dh.append(document.body,
31921                         config.autoCreate, true);
31922         }else{
31923             this.el = dh.append(document.body,
31924                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31925         }
31926     }
31927     el = this.el;
31928     el.setDisplayed(true);
31929     el.hide = this.hideAction;
31930     this.id = el.id;
31931     el.addClass("x-dlg");
31932
31933     Roo.apply(this, config);
31934
31935     this.proxy = el.createProxy("x-dlg-proxy");
31936     this.proxy.hide = this.hideAction;
31937     this.proxy.setOpacity(.5);
31938     this.proxy.hide();
31939
31940     if(config.width){
31941         el.setWidth(config.width);
31942     }
31943     if(config.height){
31944         el.setHeight(config.height);
31945     }
31946     this.size = el.getSize();
31947     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31948         this.xy = [config.x,config.y];
31949     }else{
31950         this.xy = el.getCenterXY(true);
31951     }
31952     /** The header element @type Roo.Element */
31953     this.header = el.child("> .x-dlg-hd");
31954     /** The body element @type Roo.Element */
31955     this.body = el.child("> .x-dlg-bd");
31956     /** The footer element @type Roo.Element */
31957     this.footer = el.child("> .x-dlg-ft");
31958
31959     if(!this.header){
31960         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31961     }
31962     if(!this.body){
31963         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31964     }
31965
31966     this.header.unselectable();
31967     if(this.title){
31968         this.header.update(this.title);
31969     }
31970     // this element allows the dialog to be focused for keyboard event
31971     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31972     this.focusEl.swallowEvent("click", true);
31973
31974     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31975
31976     // wrap the body and footer for special rendering
31977     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31978     if(this.footer){
31979         this.bwrap.dom.appendChild(this.footer.dom);
31980     }
31981
31982     this.bg = this.el.createChild({
31983         tag: "div", cls:"x-dlg-bg",
31984         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31985     });
31986     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31987
31988
31989     if(this.autoScroll !== false && !this.autoTabs){
31990         this.body.setStyle("overflow", "auto");
31991     }
31992
31993     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31994
31995     if(this.closable !== false){
31996         this.el.addClass("x-dlg-closable");
31997         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31998         this.close.on("click", this.closeClick, this);
31999         this.close.addClassOnOver("x-dlg-close-over");
32000     }
32001     if(this.collapsible !== false){
32002         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32003         this.collapseBtn.on("click", this.collapseClick, this);
32004         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32005         this.header.on("dblclick", this.collapseClick, this);
32006     }
32007     if(this.resizable !== false){
32008         this.el.addClass("x-dlg-resizable");
32009         this.resizer = new Roo.Resizable(el, {
32010             minWidth: this.minWidth || 80,
32011             minHeight:this.minHeight || 80,
32012             handles: this.resizeHandles || "all",
32013             pinned: true
32014         });
32015         this.resizer.on("beforeresize", this.beforeResize, this);
32016         this.resizer.on("resize", this.onResize, this);
32017     }
32018     if(this.draggable !== false){
32019         el.addClass("x-dlg-draggable");
32020         if (!this.proxyDrag) {
32021             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32022         }
32023         else {
32024             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32025         }
32026         dd.setHandleElId(this.header.id);
32027         dd.endDrag = this.endMove.createDelegate(this);
32028         dd.startDrag = this.startMove.createDelegate(this);
32029         dd.onDrag = this.onDrag.createDelegate(this);
32030         dd.scroll = false;
32031         this.dd = dd;
32032     }
32033     if(this.modal){
32034         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32035         this.mask.enableDisplayMode("block");
32036         this.mask.hide();
32037         this.el.addClass("x-dlg-modal");
32038     }
32039     if(this.shadow){
32040         this.shadow = new Roo.Shadow({
32041             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32042             offset : this.shadowOffset
32043         });
32044     }else{
32045         this.shadowOffset = 0;
32046     }
32047     if(Roo.useShims && this.shim !== false){
32048         this.shim = this.el.createShim();
32049         this.shim.hide = this.hideAction;
32050         this.shim.hide();
32051     }else{
32052         this.shim = false;
32053     }
32054     if(this.autoTabs){
32055         this.initTabs();
32056     }
32057     if (this.buttons) { 
32058         var bts= this.buttons;
32059         this.buttons = [];
32060         Roo.each(bts, function(b) {
32061             this.addButton(b);
32062         }, this);
32063     }
32064     
32065     
32066     this.addEvents({
32067         /**
32068          * @event keydown
32069          * Fires when a key is pressed
32070          * @param {Roo.BasicDialog} this
32071          * @param {Roo.EventObject} e
32072          */
32073         "keydown" : true,
32074         /**
32075          * @event move
32076          * Fires when this dialog is moved by the user.
32077          * @param {Roo.BasicDialog} this
32078          * @param {Number} x The new page X
32079          * @param {Number} y The new page Y
32080          */
32081         "move" : true,
32082         /**
32083          * @event resize
32084          * Fires when this dialog is resized by the user.
32085          * @param {Roo.BasicDialog} this
32086          * @param {Number} width The new width
32087          * @param {Number} height The new height
32088          */
32089         "resize" : true,
32090         /**
32091          * @event beforehide
32092          * Fires before this dialog is hidden.
32093          * @param {Roo.BasicDialog} this
32094          */
32095         "beforehide" : true,
32096         /**
32097          * @event hide
32098          * Fires when this dialog is hidden.
32099          * @param {Roo.BasicDialog} this
32100          */
32101         "hide" : true,
32102         /**
32103          * @event beforeshow
32104          * Fires before this dialog is shown.
32105          * @param {Roo.BasicDialog} this
32106          */
32107         "beforeshow" : true,
32108         /**
32109          * @event show
32110          * Fires when this dialog is shown.
32111          * @param {Roo.BasicDialog} this
32112          */
32113         "show" : true
32114     });
32115     el.on("keydown", this.onKeyDown, this);
32116     el.on("mousedown", this.toFront, this);
32117     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32118     this.el.hide();
32119     Roo.DialogManager.register(this);
32120     Roo.BasicDialog.superclass.constructor.call(this);
32121 };
32122
32123 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32124     shadowOffset: Roo.isIE ? 6 : 5,
32125     minHeight: 80,
32126     minWidth: 200,
32127     minButtonWidth: 75,
32128     defaultButton: null,
32129     buttonAlign: "right",
32130     tabTag: 'div',
32131     firstShow: true,
32132
32133     /**
32134      * Sets the dialog title text
32135      * @param {String} text The title text to display
32136      * @return {Roo.BasicDialog} this
32137      */
32138     setTitle : function(text){
32139         this.header.update(text);
32140         return this;
32141     },
32142
32143     // private
32144     closeClick : function(){
32145         this.hide();
32146     },
32147
32148     // private
32149     collapseClick : function(){
32150         this[this.collapsed ? "expand" : "collapse"]();
32151     },
32152
32153     /**
32154      * Collapses the dialog to its minimized state (only the title bar is visible).
32155      * Equivalent to the user clicking the collapse dialog button.
32156      */
32157     collapse : function(){
32158         if(!this.collapsed){
32159             this.collapsed = true;
32160             this.el.addClass("x-dlg-collapsed");
32161             this.restoreHeight = this.el.getHeight();
32162             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32163         }
32164     },
32165
32166     /**
32167      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32168      * clicking the expand dialog button.
32169      */
32170     expand : function(){
32171         if(this.collapsed){
32172             this.collapsed = false;
32173             this.el.removeClass("x-dlg-collapsed");
32174             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32175         }
32176     },
32177
32178     /**
32179      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32180      * @return {Roo.TabPanel} The tabs component
32181      */
32182     initTabs : function(){
32183         var tabs = this.getTabs();
32184         while(tabs.getTab(0)){
32185             tabs.removeTab(0);
32186         }
32187         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32188             var dom = el.dom;
32189             tabs.addTab(Roo.id(dom), dom.title);
32190             dom.title = "";
32191         });
32192         tabs.activate(0);
32193         return tabs;
32194     },
32195
32196     // private
32197     beforeResize : function(){
32198         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32199     },
32200
32201     // private
32202     onResize : function(){
32203         this.refreshSize();
32204         this.syncBodyHeight();
32205         this.adjustAssets();
32206         this.focus();
32207         this.fireEvent("resize", this, this.size.width, this.size.height);
32208     },
32209
32210     // private
32211     onKeyDown : function(e){
32212         if(this.isVisible()){
32213             this.fireEvent("keydown", this, e);
32214         }
32215     },
32216
32217     /**
32218      * Resizes the dialog.
32219      * @param {Number} width
32220      * @param {Number} height
32221      * @return {Roo.BasicDialog} this
32222      */
32223     resizeTo : function(width, height){
32224         this.el.setSize(width, height);
32225         this.size = {width: width, height: height};
32226         this.syncBodyHeight();
32227         if(this.fixedcenter){
32228             this.center();
32229         }
32230         if(this.isVisible()){
32231             this.constrainXY();
32232             this.adjustAssets();
32233         }
32234         this.fireEvent("resize", this, width, height);
32235         return this;
32236     },
32237
32238
32239     /**
32240      * Resizes the dialog to fit the specified content size.
32241      * @param {Number} width
32242      * @param {Number} height
32243      * @return {Roo.BasicDialog} this
32244      */
32245     setContentSize : function(w, h){
32246         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32247         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32248         //if(!this.el.isBorderBox()){
32249             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32250             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32251         //}
32252         if(this.tabs){
32253             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32254             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32255         }
32256         this.resizeTo(w, h);
32257         return this;
32258     },
32259
32260     /**
32261      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32262      * executed in response to a particular key being pressed while the dialog is active.
32263      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32264      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32265      * @param {Function} fn The function to call
32266      * @param {Object} scope (optional) The scope of the function
32267      * @return {Roo.BasicDialog} this
32268      */
32269     addKeyListener : function(key, fn, scope){
32270         var keyCode, shift, ctrl, alt;
32271         if(typeof key == "object" && !(key instanceof Array)){
32272             keyCode = key["key"];
32273             shift = key["shift"];
32274             ctrl = key["ctrl"];
32275             alt = key["alt"];
32276         }else{
32277             keyCode = key;
32278         }
32279         var handler = function(dlg, e){
32280             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32281                 var k = e.getKey();
32282                 if(keyCode instanceof Array){
32283                     for(var i = 0, len = keyCode.length; i < len; i++){
32284                         if(keyCode[i] == k){
32285                           fn.call(scope || window, dlg, k, e);
32286                           return;
32287                         }
32288                     }
32289                 }else{
32290                     if(k == keyCode){
32291                         fn.call(scope || window, dlg, k, e);
32292                     }
32293                 }
32294             }
32295         };
32296         this.on("keydown", handler);
32297         return this;
32298     },
32299
32300     /**
32301      * Returns the TabPanel component (creates it if it doesn't exist).
32302      * Note: If you wish to simply check for the existence of tabs without creating them,
32303      * check for a null 'tabs' property.
32304      * @return {Roo.TabPanel} The tabs component
32305      */
32306     getTabs : function(){
32307         if(!this.tabs){
32308             this.el.addClass("x-dlg-auto-tabs");
32309             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32310             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32311         }
32312         return this.tabs;
32313     },
32314
32315     /**
32316      * Adds a button to the footer section of the dialog.
32317      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32318      * object or a valid Roo.DomHelper element config
32319      * @param {Function} handler The function called when the button is clicked
32320      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32321      * @return {Roo.Button} The new button
32322      */
32323     addButton : function(config, handler, scope){
32324         var dh = Roo.DomHelper;
32325         if(!this.footer){
32326             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32327         }
32328         if(!this.btnContainer){
32329             var tb = this.footer.createChild({
32330
32331                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32332                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32333             }, null, true);
32334             this.btnContainer = tb.firstChild.firstChild.firstChild;
32335         }
32336         var bconfig = {
32337             handler: handler,
32338             scope: scope,
32339             minWidth: this.minButtonWidth,
32340             hideParent:true
32341         };
32342         if(typeof config == "string"){
32343             bconfig.text = config;
32344         }else{
32345             if(config.tag){
32346                 bconfig.dhconfig = config;
32347             }else{
32348                 Roo.apply(bconfig, config);
32349             }
32350         }
32351         var fc = false;
32352         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32353             bconfig.position = Math.max(0, bconfig.position);
32354             fc = this.btnContainer.childNodes[bconfig.position];
32355         }
32356          
32357         var btn = new Roo.Button(
32358             fc ? 
32359                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32360                 : this.btnContainer.appendChild(document.createElement("td")),
32361             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32362             bconfig
32363         );
32364         this.syncBodyHeight();
32365         if(!this.buttons){
32366             /**
32367              * Array of all the buttons that have been added to this dialog via addButton
32368              * @type Array
32369              */
32370             this.buttons = [];
32371         }
32372         this.buttons.push(btn);
32373         return btn;
32374     },
32375
32376     /**
32377      * Sets the default button to be focused when the dialog is displayed.
32378      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32379      * @return {Roo.BasicDialog} this
32380      */
32381     setDefaultButton : function(btn){
32382         this.defaultButton = btn;
32383         return this;
32384     },
32385
32386     // private
32387     getHeaderFooterHeight : function(safe){
32388         var height = 0;
32389         if(this.header){
32390            height += this.header.getHeight();
32391         }
32392         if(this.footer){
32393            var fm = this.footer.getMargins();
32394             height += (this.footer.getHeight()+fm.top+fm.bottom);
32395         }
32396         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32397         height += this.centerBg.getPadding("tb");
32398         return height;
32399     },
32400
32401     // private
32402     syncBodyHeight : function()
32403     {
32404         var bd = this.body, // the text
32405             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32406             bw = this.bwrap;
32407         var height = this.size.height - this.getHeaderFooterHeight(false);
32408         bd.setHeight(height-bd.getMargins("tb"));
32409         var hh = this.header.getHeight();
32410         var h = this.size.height-hh;
32411         cb.setHeight(h);
32412         
32413         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32414         bw.setHeight(h-cb.getPadding("tb"));
32415         
32416         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32417         bd.setWidth(bw.getWidth(true));
32418         if(this.tabs){
32419             this.tabs.syncHeight();
32420             if(Roo.isIE){
32421                 this.tabs.el.repaint();
32422             }
32423         }
32424     },
32425
32426     /**
32427      * Restores the previous state of the dialog if Roo.state is configured.
32428      * @return {Roo.BasicDialog} this
32429      */
32430     restoreState : function(){
32431         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32432         if(box && box.width){
32433             this.xy = [box.x, box.y];
32434             this.resizeTo(box.width, box.height);
32435         }
32436         return this;
32437     },
32438
32439     // private
32440     beforeShow : function(){
32441         this.expand();
32442         if(this.fixedcenter){
32443             this.xy = this.el.getCenterXY(true);
32444         }
32445         if(this.modal){
32446             Roo.get(document.body).addClass("x-body-masked");
32447             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32448             this.mask.show();
32449         }
32450         this.constrainXY();
32451     },
32452
32453     // private
32454     animShow : function(){
32455         var b = Roo.get(this.animateTarget).getBox();
32456         this.proxy.setSize(b.width, b.height);
32457         this.proxy.setLocation(b.x, b.y);
32458         this.proxy.show();
32459         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32460                     true, .35, this.showEl.createDelegate(this));
32461     },
32462
32463     /**
32464      * Shows the dialog.
32465      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32466      * @return {Roo.BasicDialog} this
32467      */
32468     show : function(animateTarget){
32469         if (this.fireEvent("beforeshow", this) === false){
32470             return;
32471         }
32472         if(this.syncHeightBeforeShow){
32473             this.syncBodyHeight();
32474         }else if(this.firstShow){
32475             this.firstShow = false;
32476             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32477         }
32478         this.animateTarget = animateTarget || this.animateTarget;
32479         if(!this.el.isVisible()){
32480             this.beforeShow();
32481             if(this.animateTarget && Roo.get(this.animateTarget)){
32482                 this.animShow();
32483             }else{
32484                 this.showEl();
32485             }
32486         }
32487         return this;
32488     },
32489
32490     // private
32491     showEl : function(){
32492         this.proxy.hide();
32493         this.el.setXY(this.xy);
32494         this.el.show();
32495         this.adjustAssets(true);
32496         this.toFront();
32497         this.focus();
32498         // IE peekaboo bug - fix found by Dave Fenwick
32499         if(Roo.isIE){
32500             this.el.repaint();
32501         }
32502         this.fireEvent("show", this);
32503     },
32504
32505     /**
32506      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32507      * dialog itself will receive focus.
32508      */
32509     focus : function(){
32510         if(this.defaultButton){
32511             this.defaultButton.focus();
32512         }else{
32513             this.focusEl.focus();
32514         }
32515     },
32516
32517     // private
32518     constrainXY : function(){
32519         if(this.constraintoviewport !== false){
32520             if(!this.viewSize){
32521                 if(this.container){
32522                     var s = this.container.getSize();
32523                     this.viewSize = [s.width, s.height];
32524                 }else{
32525                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32526                 }
32527             }
32528             var s = Roo.get(this.container||document).getScroll();
32529
32530             var x = this.xy[0], y = this.xy[1];
32531             var w = this.size.width, h = this.size.height;
32532             var vw = this.viewSize[0], vh = this.viewSize[1];
32533             // only move it if it needs it
32534             var moved = false;
32535             // first validate right/bottom
32536             if(x + w > vw+s.left){
32537                 x = vw - w;
32538                 moved = true;
32539             }
32540             if(y + h > vh+s.top){
32541                 y = vh - h;
32542                 moved = true;
32543             }
32544             // then make sure top/left isn't negative
32545             if(x < s.left){
32546                 x = s.left;
32547                 moved = true;
32548             }
32549             if(y < s.top){
32550                 y = s.top;
32551                 moved = true;
32552             }
32553             if(moved){
32554                 // cache xy
32555                 this.xy = [x, y];
32556                 if(this.isVisible()){
32557                     this.el.setLocation(x, y);
32558                     this.adjustAssets();
32559                 }
32560             }
32561         }
32562     },
32563
32564     // private
32565     onDrag : function(){
32566         if(!this.proxyDrag){
32567             this.xy = this.el.getXY();
32568             this.adjustAssets();
32569         }
32570     },
32571
32572     // private
32573     adjustAssets : function(doShow){
32574         var x = this.xy[0], y = this.xy[1];
32575         var w = this.size.width, h = this.size.height;
32576         if(doShow === true){
32577             if(this.shadow){
32578                 this.shadow.show(this.el);
32579             }
32580             if(this.shim){
32581                 this.shim.show();
32582             }
32583         }
32584         if(this.shadow && this.shadow.isVisible()){
32585             this.shadow.show(this.el);
32586         }
32587         if(this.shim && this.shim.isVisible()){
32588             this.shim.setBounds(x, y, w, h);
32589         }
32590     },
32591
32592     // private
32593     adjustViewport : function(w, h){
32594         if(!w || !h){
32595             w = Roo.lib.Dom.getViewWidth();
32596             h = Roo.lib.Dom.getViewHeight();
32597         }
32598         // cache the size
32599         this.viewSize = [w, h];
32600         if(this.modal && this.mask.isVisible()){
32601             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32602             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32603         }
32604         if(this.isVisible()){
32605             this.constrainXY();
32606         }
32607     },
32608
32609     /**
32610      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32611      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32612      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32613      */
32614     destroy : function(removeEl){
32615         if(this.isVisible()){
32616             this.animateTarget = null;
32617             this.hide();
32618         }
32619         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32620         if(this.tabs){
32621             this.tabs.destroy(removeEl);
32622         }
32623         Roo.destroy(
32624              this.shim,
32625              this.proxy,
32626              this.resizer,
32627              this.close,
32628              this.mask
32629         );
32630         if(this.dd){
32631             this.dd.unreg();
32632         }
32633         if(this.buttons){
32634            for(var i = 0, len = this.buttons.length; i < len; i++){
32635                this.buttons[i].destroy();
32636            }
32637         }
32638         this.el.removeAllListeners();
32639         if(removeEl === true){
32640             this.el.update("");
32641             this.el.remove();
32642         }
32643         Roo.DialogManager.unregister(this);
32644     },
32645
32646     // private
32647     startMove : function(){
32648         if(this.proxyDrag){
32649             this.proxy.show();
32650         }
32651         if(this.constraintoviewport !== false){
32652             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32653         }
32654     },
32655
32656     // private
32657     endMove : function(){
32658         if(!this.proxyDrag){
32659             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32660         }else{
32661             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32662             this.proxy.hide();
32663         }
32664         this.refreshSize();
32665         this.adjustAssets();
32666         this.focus();
32667         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32668     },
32669
32670     /**
32671      * Brings this dialog to the front of any other visible dialogs
32672      * @return {Roo.BasicDialog} this
32673      */
32674     toFront : function(){
32675         Roo.DialogManager.bringToFront(this);
32676         return this;
32677     },
32678
32679     /**
32680      * Sends this dialog to the back (under) of any other visible dialogs
32681      * @return {Roo.BasicDialog} this
32682      */
32683     toBack : function(){
32684         Roo.DialogManager.sendToBack(this);
32685         return this;
32686     },
32687
32688     /**
32689      * Centers this dialog in the viewport
32690      * @return {Roo.BasicDialog} this
32691      */
32692     center : function(){
32693         var xy = this.el.getCenterXY(true);
32694         this.moveTo(xy[0], xy[1]);
32695         return this;
32696     },
32697
32698     /**
32699      * Moves the dialog's top-left corner to the specified point
32700      * @param {Number} x
32701      * @param {Number} y
32702      * @return {Roo.BasicDialog} this
32703      */
32704     moveTo : function(x, y){
32705         this.xy = [x,y];
32706         if(this.isVisible()){
32707             this.el.setXY(this.xy);
32708             this.adjustAssets();
32709         }
32710         return this;
32711     },
32712
32713     /**
32714      * Aligns the dialog to the specified element
32715      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32716      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32717      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32718      * @return {Roo.BasicDialog} this
32719      */
32720     alignTo : function(element, position, offsets){
32721         this.xy = this.el.getAlignToXY(element, position, offsets);
32722         if(this.isVisible()){
32723             this.el.setXY(this.xy);
32724             this.adjustAssets();
32725         }
32726         return this;
32727     },
32728
32729     /**
32730      * Anchors an element to another element and realigns it when the window is resized.
32731      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32732      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32733      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32734      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32735      * is a number, it is used as the buffer delay (defaults to 50ms).
32736      * @return {Roo.BasicDialog} this
32737      */
32738     anchorTo : function(el, alignment, offsets, monitorScroll){
32739         var action = function(){
32740             this.alignTo(el, alignment, offsets);
32741         };
32742         Roo.EventManager.onWindowResize(action, this);
32743         var tm = typeof monitorScroll;
32744         if(tm != 'undefined'){
32745             Roo.EventManager.on(window, 'scroll', action, this,
32746                 {buffer: tm == 'number' ? monitorScroll : 50});
32747         }
32748         action.call(this);
32749         return this;
32750     },
32751
32752     /**
32753      * Returns true if the dialog is visible
32754      * @return {Boolean}
32755      */
32756     isVisible : function(){
32757         return this.el.isVisible();
32758     },
32759
32760     // private
32761     animHide : function(callback){
32762         var b = Roo.get(this.animateTarget).getBox();
32763         this.proxy.show();
32764         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32765         this.el.hide();
32766         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32767                     this.hideEl.createDelegate(this, [callback]));
32768     },
32769
32770     /**
32771      * Hides the dialog.
32772      * @param {Function} callback (optional) Function to call when the dialog is hidden
32773      * @return {Roo.BasicDialog} this
32774      */
32775     hide : function(callback){
32776         if (this.fireEvent("beforehide", this) === false){
32777             return;
32778         }
32779         if(this.shadow){
32780             this.shadow.hide();
32781         }
32782         if(this.shim) {
32783           this.shim.hide();
32784         }
32785         // sometimes animateTarget seems to get set.. causing problems...
32786         // this just double checks..
32787         if(this.animateTarget && Roo.get(this.animateTarget)) {
32788            this.animHide(callback);
32789         }else{
32790             this.el.hide();
32791             this.hideEl(callback);
32792         }
32793         return this;
32794     },
32795
32796     // private
32797     hideEl : function(callback){
32798         this.proxy.hide();
32799         if(this.modal){
32800             this.mask.hide();
32801             Roo.get(document.body).removeClass("x-body-masked");
32802         }
32803         this.fireEvent("hide", this);
32804         if(typeof callback == "function"){
32805             callback();
32806         }
32807     },
32808
32809     // private
32810     hideAction : function(){
32811         this.setLeft("-10000px");
32812         this.setTop("-10000px");
32813         this.setStyle("visibility", "hidden");
32814     },
32815
32816     // private
32817     refreshSize : function(){
32818         this.size = this.el.getSize();
32819         this.xy = this.el.getXY();
32820         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32821     },
32822
32823     // private
32824     // z-index is managed by the DialogManager and may be overwritten at any time
32825     setZIndex : function(index){
32826         if(this.modal){
32827             this.mask.setStyle("z-index", index);
32828         }
32829         if(this.shim){
32830             this.shim.setStyle("z-index", ++index);
32831         }
32832         if(this.shadow){
32833             this.shadow.setZIndex(++index);
32834         }
32835         this.el.setStyle("z-index", ++index);
32836         if(this.proxy){
32837             this.proxy.setStyle("z-index", ++index);
32838         }
32839         if(this.resizer){
32840             this.resizer.proxy.setStyle("z-index", ++index);
32841         }
32842
32843         this.lastZIndex = index;
32844     },
32845
32846     /**
32847      * Returns the element for this dialog
32848      * @return {Roo.Element} The underlying dialog Element
32849      */
32850     getEl : function(){
32851         return this.el;
32852     }
32853 });
32854
32855 /**
32856  * @class Roo.DialogManager
32857  * Provides global access to BasicDialogs that have been created and
32858  * support for z-indexing (layering) multiple open dialogs.
32859  */
32860 Roo.DialogManager = function(){
32861     var list = {};
32862     var accessList = [];
32863     var front = null;
32864
32865     // private
32866     var sortDialogs = function(d1, d2){
32867         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32868     };
32869
32870     // private
32871     var orderDialogs = function(){
32872         accessList.sort(sortDialogs);
32873         var seed = Roo.DialogManager.zseed;
32874         for(var i = 0, len = accessList.length; i < len; i++){
32875             var dlg = accessList[i];
32876             if(dlg){
32877                 dlg.setZIndex(seed + (i*10));
32878             }
32879         }
32880     };
32881
32882     return {
32883         /**
32884          * The starting z-index for BasicDialogs (defaults to 9000)
32885          * @type Number The z-index value
32886          */
32887         zseed : 9000,
32888
32889         // private
32890         register : function(dlg){
32891             list[dlg.id] = dlg;
32892             accessList.push(dlg);
32893         },
32894
32895         // private
32896         unregister : function(dlg){
32897             delete list[dlg.id];
32898             var i=0;
32899             var len=0;
32900             if(!accessList.indexOf){
32901                 for(  i = 0, len = accessList.length; i < len; i++){
32902                     if(accessList[i] == dlg){
32903                         accessList.splice(i, 1);
32904                         return;
32905                     }
32906                 }
32907             }else{
32908                  i = accessList.indexOf(dlg);
32909                 if(i != -1){
32910                     accessList.splice(i, 1);
32911                 }
32912             }
32913         },
32914
32915         /**
32916          * Gets a registered dialog by id
32917          * @param {String/Object} id The id of the dialog or a dialog
32918          * @return {Roo.BasicDialog} this
32919          */
32920         get : function(id){
32921             return typeof id == "object" ? id : list[id];
32922         },
32923
32924         /**
32925          * Brings the specified dialog to the front
32926          * @param {String/Object} dlg The id of the dialog or a dialog
32927          * @return {Roo.BasicDialog} this
32928          */
32929         bringToFront : function(dlg){
32930             dlg = this.get(dlg);
32931             if(dlg != front){
32932                 front = dlg;
32933                 dlg._lastAccess = new Date().getTime();
32934                 orderDialogs();
32935             }
32936             return dlg;
32937         },
32938
32939         /**
32940          * Sends the specified dialog to the back
32941          * @param {String/Object} dlg The id of the dialog or a dialog
32942          * @return {Roo.BasicDialog} this
32943          */
32944         sendToBack : function(dlg){
32945             dlg = this.get(dlg);
32946             dlg._lastAccess = -(new Date().getTime());
32947             orderDialogs();
32948             return dlg;
32949         },
32950
32951         /**
32952          * Hides all dialogs
32953          */
32954         hideAll : function(){
32955             for(var id in list){
32956                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32957                     list[id].hide();
32958                 }
32959             }
32960         }
32961     };
32962 }();
32963
32964 /**
32965  * @class Roo.LayoutDialog
32966  * @extends Roo.BasicDialog
32967  * Dialog which provides adjustments for working with a layout in a Dialog.
32968  * Add your necessary layout config options to the dialog's config.<br>
32969  * Example usage (including a nested layout):
32970  * <pre><code>
32971 if(!dialog){
32972     dialog = new Roo.LayoutDialog("download-dlg", {
32973         modal: true,
32974         width:600,
32975         height:450,
32976         shadow:true,
32977         minWidth:500,
32978         minHeight:350,
32979         autoTabs:true,
32980         proxyDrag:true,
32981         // layout config merges with the dialog config
32982         center:{
32983             tabPosition: "top",
32984             alwaysShowTabs: true
32985         }
32986     });
32987     dialog.addKeyListener(27, dialog.hide, dialog);
32988     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32989     dialog.addButton("Build It!", this.getDownload, this);
32990
32991     // we can even add nested layouts
32992     var innerLayout = new Roo.BorderLayout("dl-inner", {
32993         east: {
32994             initialSize: 200,
32995             autoScroll:true,
32996             split:true
32997         },
32998         center: {
32999             autoScroll:true
33000         }
33001     });
33002     innerLayout.beginUpdate();
33003     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33004     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33005     innerLayout.endUpdate(true);
33006
33007     var layout = dialog.getLayout();
33008     layout.beginUpdate();
33009     layout.add("center", new Roo.ContentPanel("standard-panel",
33010                         {title: "Download the Source", fitToFrame:true}));
33011     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33012                {title: "Build your own roo.js"}));
33013     layout.getRegion("center").showPanel(sp);
33014     layout.endUpdate();
33015 }
33016 </code></pre>
33017     * @constructor
33018     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33019     * @param {Object} config configuration options
33020   */
33021 Roo.LayoutDialog = function(el, cfg){
33022     
33023     var config=  cfg;
33024     if (typeof(cfg) == 'undefined') {
33025         config = Roo.apply({}, el);
33026         // not sure why we use documentElement here.. - it should always be body.
33027         // IE7 borks horribly if we use documentElement.
33028         // webkit also does not like documentElement - it creates a body element...
33029         el = Roo.get( document.body || document.documentElement ).createChild();
33030         //config.autoCreate = true;
33031     }
33032     
33033     
33034     config.autoTabs = false;
33035     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33036     this.body.setStyle({overflow:"hidden", position:"relative"});
33037     this.layout = new Roo.BorderLayout(this.body.dom, config);
33038     this.layout.monitorWindowResize = false;
33039     this.el.addClass("x-dlg-auto-layout");
33040     // fix case when center region overwrites center function
33041     this.center = Roo.BasicDialog.prototype.center;
33042     this.on("show", this.layout.layout, this.layout, true);
33043     if (config.items) {
33044         var xitems = config.items;
33045         delete config.items;
33046         Roo.each(xitems, this.addxtype, this);
33047     }
33048     
33049     
33050 };
33051 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33052     /**
33053      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33054      * @deprecated
33055      */
33056     endUpdate : function(){
33057         this.layout.endUpdate();
33058     },
33059
33060     /**
33061      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33062      *  @deprecated
33063      */
33064     beginUpdate : function(){
33065         this.layout.beginUpdate();
33066     },
33067
33068     /**
33069      * Get the BorderLayout for this dialog
33070      * @return {Roo.BorderLayout}
33071      */
33072     getLayout : function(){
33073         return this.layout;
33074     },
33075
33076     showEl : function(){
33077         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33078         if(Roo.isIE7){
33079             this.layout.layout();
33080         }
33081     },
33082
33083     // private
33084     // Use the syncHeightBeforeShow config option to control this automatically
33085     syncBodyHeight : function(){
33086         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33087         if(this.layout){this.layout.layout();}
33088     },
33089     
33090       /**
33091      * Add an xtype element (actually adds to the layout.)
33092      * @return {Object} xdata xtype object data.
33093      */
33094     
33095     addxtype : function(c) {
33096         return this.layout.addxtype(c);
33097     }
33098 });/*
33099  * Based on:
33100  * Ext JS Library 1.1.1
33101  * Copyright(c) 2006-2007, Ext JS, LLC.
33102  *
33103  * Originally Released Under LGPL - original licence link has changed is not relivant.
33104  *
33105  * Fork - LGPL
33106  * <script type="text/javascript">
33107  */
33108  
33109 /**
33110  * @class Roo.MessageBox
33111  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33112  * Example usage:
33113  *<pre><code>
33114 // Basic alert:
33115 Roo.Msg.alert('Status', 'Changes saved successfully.');
33116
33117 // Prompt for user data:
33118 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33119     if (btn == 'ok'){
33120         // process text value...
33121     }
33122 });
33123
33124 // Show a dialog using config options:
33125 Roo.Msg.show({
33126    title:'Save Changes?',
33127    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33128    buttons: Roo.Msg.YESNOCANCEL,
33129    fn: processResult,
33130    animEl: 'elId'
33131 });
33132 </code></pre>
33133  * @singleton
33134  */
33135 Roo.MessageBox = function(){
33136     var dlg, opt, mask, waitTimer;
33137     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33138     var buttons, activeTextEl, bwidth;
33139
33140     // private
33141     var handleButton = function(button){
33142         dlg.hide();
33143         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33144     };
33145
33146     // private
33147     var handleHide = function(){
33148         if(opt && opt.cls){
33149             dlg.el.removeClass(opt.cls);
33150         }
33151         if(waitTimer){
33152             Roo.TaskMgr.stop(waitTimer);
33153             waitTimer = null;
33154         }
33155     };
33156
33157     // private
33158     var updateButtons = function(b){
33159         var width = 0;
33160         if(!b){
33161             buttons["ok"].hide();
33162             buttons["cancel"].hide();
33163             buttons["yes"].hide();
33164             buttons["no"].hide();
33165             dlg.footer.dom.style.display = 'none';
33166             return width;
33167         }
33168         dlg.footer.dom.style.display = '';
33169         for(var k in buttons){
33170             if(typeof buttons[k] != "function"){
33171                 if(b[k]){
33172                     buttons[k].show();
33173                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33174                     width += buttons[k].el.getWidth()+15;
33175                 }else{
33176                     buttons[k].hide();
33177                 }
33178             }
33179         }
33180         return width;
33181     };
33182
33183     // private
33184     var handleEsc = function(d, k, e){
33185         if(opt && opt.closable !== false){
33186             dlg.hide();
33187         }
33188         if(e){
33189             e.stopEvent();
33190         }
33191     };
33192
33193     return {
33194         /**
33195          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33196          * @return {Roo.BasicDialog} The BasicDialog element
33197          */
33198         getDialog : function(){
33199            if(!dlg){
33200                 dlg = new Roo.BasicDialog("x-msg-box", {
33201                     autoCreate : true,
33202                     shadow: true,
33203                     draggable: true,
33204                     resizable:false,
33205                     constraintoviewport:false,
33206                     fixedcenter:true,
33207                     collapsible : false,
33208                     shim:true,
33209                     modal: true,
33210                     width:400, height:100,
33211                     buttonAlign:"center",
33212                     closeClick : function(){
33213                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33214                             handleButton("no");
33215                         }else{
33216                             handleButton("cancel");
33217                         }
33218                     }
33219                 });
33220                 dlg.on("hide", handleHide);
33221                 mask = dlg.mask;
33222                 dlg.addKeyListener(27, handleEsc);
33223                 buttons = {};
33224                 var bt = this.buttonText;
33225                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33226                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33227                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33228                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33229                 bodyEl = dlg.body.createChild({
33230
33231                     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>'
33232                 });
33233                 msgEl = bodyEl.dom.firstChild;
33234                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33235                 textboxEl.enableDisplayMode();
33236                 textboxEl.addKeyListener([10,13], function(){
33237                     if(dlg.isVisible() && opt && opt.buttons){
33238                         if(opt.buttons.ok){
33239                             handleButton("ok");
33240                         }else if(opt.buttons.yes){
33241                             handleButton("yes");
33242                         }
33243                     }
33244                 });
33245                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33246                 textareaEl.enableDisplayMode();
33247                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33248                 progressEl.enableDisplayMode();
33249                 var pf = progressEl.dom.firstChild;
33250                 if (pf) {
33251                     pp = Roo.get(pf.firstChild);
33252                     pp.setHeight(pf.offsetHeight);
33253                 }
33254                 
33255             }
33256             return dlg;
33257         },
33258
33259         /**
33260          * Updates the message box body text
33261          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33262          * the XHTML-compliant non-breaking space character '&amp;#160;')
33263          * @return {Roo.MessageBox} This message box
33264          */
33265         updateText : function(text){
33266             if(!dlg.isVisible() && !opt.width){
33267                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33268             }
33269             msgEl.innerHTML = text || '&#160;';
33270       
33271             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33272             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33273             var w = Math.max(
33274                     Math.min(opt.width || cw , this.maxWidth), 
33275                     Math.max(opt.minWidth || this.minWidth, bwidth)
33276             );
33277             if(opt.prompt){
33278                 activeTextEl.setWidth(w);
33279             }
33280             if(dlg.isVisible()){
33281                 dlg.fixedcenter = false;
33282             }
33283             // to big, make it scroll. = But as usual stupid IE does not support
33284             // !important..
33285             
33286             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33287                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33288                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33289             } else {
33290                 bodyEl.dom.style.height = '';
33291                 bodyEl.dom.style.overflowY = '';
33292             }
33293             if (cw > w) {
33294                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33295             } else {
33296                 bodyEl.dom.style.overflowX = '';
33297             }
33298             
33299             dlg.setContentSize(w, bodyEl.getHeight());
33300             if(dlg.isVisible()){
33301                 dlg.fixedcenter = true;
33302             }
33303             return this;
33304         },
33305
33306         /**
33307          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33308          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33309          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33310          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33311          * @return {Roo.MessageBox} This message box
33312          */
33313         updateProgress : function(value, text){
33314             if(text){
33315                 this.updateText(text);
33316             }
33317             if (pp) { // weird bug on my firefox - for some reason this is not defined
33318                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33319             }
33320             return this;
33321         },        
33322
33323         /**
33324          * Returns true if the message box is currently displayed
33325          * @return {Boolean} True if the message box is visible, else false
33326          */
33327         isVisible : function(){
33328             return dlg && dlg.isVisible();  
33329         },
33330
33331         /**
33332          * Hides the message box if it is displayed
33333          */
33334         hide : function(){
33335             if(this.isVisible()){
33336                 dlg.hide();
33337             }  
33338         },
33339
33340         /**
33341          * Displays a new message box, or reinitializes an existing message box, based on the config options
33342          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33343          * The following config object properties are supported:
33344          * <pre>
33345 Property    Type             Description
33346 ----------  ---------------  ------------------------------------------------------------------------------------
33347 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33348                                    closes (defaults to undefined)
33349 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33350                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33351 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33352                                    progress and wait dialogs will ignore this property and always hide the
33353                                    close button as they can only be closed programmatically.
33354 cls               String           A custom CSS class to apply to the message box element
33355 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33356                                    displayed (defaults to 75)
33357 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33358                                    function will be btn (the name of the button that was clicked, if applicable,
33359                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33360                                    Progress and wait dialogs will ignore this option since they do not respond to
33361                                    user actions and can only be closed programmatically, so any required function
33362                                    should be called by the same code after it closes the dialog.
33363 icon              String           A CSS class that provides a background image to be used as an icon for
33364                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33365 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33366 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33367 modal             Boolean          False to allow user interaction with the page while the message box is
33368                                    displayed (defaults to true)
33369 msg               String           A string that will replace the existing message box body text (defaults
33370                                    to the XHTML-compliant non-breaking space character '&#160;')
33371 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33372 progress          Boolean          True to display a progress bar (defaults to false)
33373 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33374 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33375 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33376 title             String           The title text
33377 value             String           The string value to set into the active textbox element if displayed
33378 wait              Boolean          True to display a progress bar (defaults to false)
33379 width             Number           The width of the dialog in pixels
33380 </pre>
33381          *
33382          * Example usage:
33383          * <pre><code>
33384 Roo.Msg.show({
33385    title: 'Address',
33386    msg: 'Please enter your address:',
33387    width: 300,
33388    buttons: Roo.MessageBox.OKCANCEL,
33389    multiline: true,
33390    fn: saveAddress,
33391    animEl: 'addAddressBtn'
33392 });
33393 </code></pre>
33394          * @param {Object} config Configuration options
33395          * @return {Roo.MessageBox} This message box
33396          */
33397         show : function(options)
33398         {
33399             
33400             // this causes nightmares if you show one dialog after another
33401             // especially on callbacks..
33402              
33403             if(this.isVisible()){
33404                 
33405                 this.hide();
33406                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33407                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33408                 Roo.log("New Dialog Message:" +  options.msg )
33409                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33410                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33411                 
33412             }
33413             var d = this.getDialog();
33414             opt = options;
33415             d.setTitle(opt.title || "&#160;");
33416             d.close.setDisplayed(opt.closable !== false);
33417             activeTextEl = textboxEl;
33418             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33419             if(opt.prompt){
33420                 if(opt.multiline){
33421                     textboxEl.hide();
33422                     textareaEl.show();
33423                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33424                         opt.multiline : this.defaultTextHeight);
33425                     activeTextEl = textareaEl;
33426                 }else{
33427                     textboxEl.show();
33428                     textareaEl.hide();
33429                 }
33430             }else{
33431                 textboxEl.hide();
33432                 textareaEl.hide();
33433             }
33434             progressEl.setDisplayed(opt.progress === true);
33435             this.updateProgress(0);
33436             activeTextEl.dom.value = opt.value || "";
33437             if(opt.prompt){
33438                 dlg.setDefaultButton(activeTextEl);
33439             }else{
33440                 var bs = opt.buttons;
33441                 var db = null;
33442                 if(bs && bs.ok){
33443                     db = buttons["ok"];
33444                 }else if(bs && bs.yes){
33445                     db = buttons["yes"];
33446                 }
33447                 dlg.setDefaultButton(db);
33448             }
33449             bwidth = updateButtons(opt.buttons);
33450             this.updateText(opt.msg);
33451             if(opt.cls){
33452                 d.el.addClass(opt.cls);
33453             }
33454             d.proxyDrag = opt.proxyDrag === true;
33455             d.modal = opt.modal !== false;
33456             d.mask = opt.modal !== false ? mask : false;
33457             if(!d.isVisible()){
33458                 // force it to the end of the z-index stack so it gets a cursor in FF
33459                 document.body.appendChild(dlg.el.dom);
33460                 d.animateTarget = null;
33461                 d.show(options.animEl);
33462             }
33463             return this;
33464         },
33465
33466         /**
33467          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33468          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33469          * and closing the message box when the process is complete.
33470          * @param {String} title The title bar text
33471          * @param {String} msg The message box body text
33472          * @return {Roo.MessageBox} This message box
33473          */
33474         progress : function(title, msg){
33475             this.show({
33476                 title : title,
33477                 msg : msg,
33478                 buttons: false,
33479                 progress:true,
33480                 closable:false,
33481                 minWidth: this.minProgressWidth,
33482                 modal : true
33483             });
33484             return this;
33485         },
33486
33487         /**
33488          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33489          * If a callback function is passed it will be called after the user clicks the button, and the
33490          * id of the button that was clicked will be passed as the only parameter to the callback
33491          * (could also be the top-right close button).
33492          * @param {String} title The title bar text
33493          * @param {String} msg The message box body text
33494          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33495          * @param {Object} scope (optional) The scope of the callback function
33496          * @return {Roo.MessageBox} This message box
33497          */
33498         alert : function(title, msg, fn, scope){
33499             this.show({
33500                 title : title,
33501                 msg : msg,
33502                 buttons: this.OK,
33503                 fn: fn,
33504                 scope : scope,
33505                 modal : true
33506             });
33507             return this;
33508         },
33509
33510         /**
33511          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33512          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33513          * You are responsible for closing the message box when the process is complete.
33514          * @param {String} msg The message box body text
33515          * @param {String} title (optional) The title bar text
33516          * @return {Roo.MessageBox} This message box
33517          */
33518         wait : function(msg, title){
33519             this.show({
33520                 title : title,
33521                 msg : msg,
33522                 buttons: false,
33523                 closable:false,
33524                 progress:true,
33525                 modal:true,
33526                 width:300,
33527                 wait:true
33528             });
33529             waitTimer = Roo.TaskMgr.start({
33530                 run: function(i){
33531                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33532                 },
33533                 interval: 1000
33534             });
33535             return this;
33536         },
33537
33538         /**
33539          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33540          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33541          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33542          * @param {String} title The title bar text
33543          * @param {String} msg The message box body text
33544          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33545          * @param {Object} scope (optional) The scope of the callback function
33546          * @return {Roo.MessageBox} This message box
33547          */
33548         confirm : function(title, msg, fn, scope){
33549             this.show({
33550                 title : title,
33551                 msg : msg,
33552                 buttons: this.YESNO,
33553                 fn: fn,
33554                 scope : scope,
33555                 modal : true
33556             });
33557             return this;
33558         },
33559
33560         /**
33561          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33562          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33563          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33564          * (could also be the top-right close button) and the text that was entered will be passed as the two
33565          * parameters to the callback.
33566          * @param {String} title The title bar text
33567          * @param {String} msg The message box body text
33568          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33569          * @param {Object} scope (optional) The scope of the callback function
33570          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33571          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33572          * @return {Roo.MessageBox} This message box
33573          */
33574         prompt : function(title, msg, fn, scope, multiline){
33575             this.show({
33576                 title : title,
33577                 msg : msg,
33578                 buttons: this.OKCANCEL,
33579                 fn: fn,
33580                 minWidth:250,
33581                 scope : scope,
33582                 prompt:true,
33583                 multiline: multiline,
33584                 modal : true
33585             });
33586             return this;
33587         },
33588
33589         /**
33590          * Button config that displays a single OK button
33591          * @type Object
33592          */
33593         OK : {ok:true},
33594         /**
33595          * Button config that displays Yes and No buttons
33596          * @type Object
33597          */
33598         YESNO : {yes:true, no:true},
33599         /**
33600          * Button config that displays OK and Cancel buttons
33601          * @type Object
33602          */
33603         OKCANCEL : {ok:true, cancel:true},
33604         /**
33605          * Button config that displays Yes, No and Cancel buttons
33606          * @type Object
33607          */
33608         YESNOCANCEL : {yes:true, no:true, cancel:true},
33609
33610         /**
33611          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33612          * @type Number
33613          */
33614         defaultTextHeight : 75,
33615         /**
33616          * The maximum width in pixels of the message box (defaults to 600)
33617          * @type Number
33618          */
33619         maxWidth : 600,
33620         /**
33621          * The minimum width in pixels of the message box (defaults to 100)
33622          * @type Number
33623          */
33624         minWidth : 100,
33625         /**
33626          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33627          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33628          * @type Number
33629          */
33630         minProgressWidth : 250,
33631         /**
33632          * An object containing the default button text strings that can be overriden for localized language support.
33633          * Supported properties are: ok, cancel, yes and no.
33634          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33635          * @type Object
33636          */
33637         buttonText : {
33638             ok : "OK",
33639             cancel : "Cancel",
33640             yes : "Yes",
33641             no : "No"
33642         }
33643     };
33644 }();
33645
33646 /**
33647  * Shorthand for {@link Roo.MessageBox}
33648  */
33649 Roo.Msg = Roo.MessageBox;/*
33650  * Based on:
33651  * Ext JS Library 1.1.1
33652  * Copyright(c) 2006-2007, Ext JS, LLC.
33653  *
33654  * Originally Released Under LGPL - original licence link has changed is not relivant.
33655  *
33656  * Fork - LGPL
33657  * <script type="text/javascript">
33658  */
33659 /**
33660  * @class Roo.QuickTips
33661  * Provides attractive and customizable tooltips for any element.
33662  * @singleton
33663  */
33664 Roo.QuickTips = function(){
33665     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33666     var ce, bd, xy, dd;
33667     var visible = false, disabled = true, inited = false;
33668     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33669     
33670     var onOver = function(e){
33671         if(disabled){
33672             return;
33673         }
33674         var t = e.getTarget();
33675         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33676             return;
33677         }
33678         if(ce && t == ce.el){
33679             clearTimeout(hideProc);
33680             return;
33681         }
33682         if(t && tagEls[t.id]){
33683             tagEls[t.id].el = t;
33684             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33685             return;
33686         }
33687         var ttp, et = Roo.fly(t);
33688         var ns = cfg.namespace;
33689         if(tm.interceptTitles && t.title){
33690             ttp = t.title;
33691             t.qtip = ttp;
33692             t.removeAttribute("title");
33693             e.preventDefault();
33694         }else{
33695             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33696         }
33697         if(ttp){
33698             showProc = show.defer(tm.showDelay, tm, [{
33699                 el: t, 
33700                 text: ttp.replace(/\\n/g,'<br/>'),
33701                 width: et.getAttributeNS(ns, cfg.width),
33702                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33703                 title: et.getAttributeNS(ns, cfg.title),
33704                     cls: et.getAttributeNS(ns, cfg.cls)
33705             }]);
33706         }
33707     };
33708     
33709     var onOut = function(e){
33710         clearTimeout(showProc);
33711         var t = e.getTarget();
33712         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33713             hideProc = setTimeout(hide, tm.hideDelay);
33714         }
33715     };
33716     
33717     var onMove = function(e){
33718         if(disabled){
33719             return;
33720         }
33721         xy = e.getXY();
33722         xy[1] += 18;
33723         if(tm.trackMouse && ce){
33724             el.setXY(xy);
33725         }
33726     };
33727     
33728     var onDown = function(e){
33729         clearTimeout(showProc);
33730         clearTimeout(hideProc);
33731         if(!e.within(el)){
33732             if(tm.hideOnClick){
33733                 hide();
33734                 tm.disable();
33735                 tm.enable.defer(100, tm);
33736             }
33737         }
33738     };
33739     
33740     var getPad = function(){
33741         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33742     };
33743
33744     var show = function(o){
33745         if(disabled){
33746             return;
33747         }
33748         clearTimeout(dismissProc);
33749         ce = o;
33750         if(removeCls){ // in case manually hidden
33751             el.removeClass(removeCls);
33752             removeCls = null;
33753         }
33754         if(ce.cls){
33755             el.addClass(ce.cls);
33756             removeCls = ce.cls;
33757         }
33758         if(ce.title){
33759             tipTitle.update(ce.title);
33760             tipTitle.show();
33761         }else{
33762             tipTitle.update('');
33763             tipTitle.hide();
33764         }
33765         el.dom.style.width  = tm.maxWidth+'px';
33766         //tipBody.dom.style.width = '';
33767         tipBodyText.update(o.text);
33768         var p = getPad(), w = ce.width;
33769         if(!w){
33770             var td = tipBodyText.dom;
33771             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33772             if(aw > tm.maxWidth){
33773                 w = tm.maxWidth;
33774             }else if(aw < tm.minWidth){
33775                 w = tm.minWidth;
33776             }else{
33777                 w = aw;
33778             }
33779         }
33780         //tipBody.setWidth(w);
33781         el.setWidth(parseInt(w, 10) + p);
33782         if(ce.autoHide === false){
33783             close.setDisplayed(true);
33784             if(dd){
33785                 dd.unlock();
33786             }
33787         }else{
33788             close.setDisplayed(false);
33789             if(dd){
33790                 dd.lock();
33791             }
33792         }
33793         if(xy){
33794             el.avoidY = xy[1]-18;
33795             el.setXY(xy);
33796         }
33797         if(tm.animate){
33798             el.setOpacity(.1);
33799             el.setStyle("visibility", "visible");
33800             el.fadeIn({callback: afterShow});
33801         }else{
33802             afterShow();
33803         }
33804     };
33805     
33806     var afterShow = function(){
33807         if(ce){
33808             el.show();
33809             esc.enable();
33810             if(tm.autoDismiss && ce.autoHide !== false){
33811                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33812             }
33813         }
33814     };
33815     
33816     var hide = function(noanim){
33817         clearTimeout(dismissProc);
33818         clearTimeout(hideProc);
33819         ce = null;
33820         if(el.isVisible()){
33821             esc.disable();
33822             if(noanim !== true && tm.animate){
33823                 el.fadeOut({callback: afterHide});
33824             }else{
33825                 afterHide();
33826             } 
33827         }
33828     };
33829     
33830     var afterHide = function(){
33831         el.hide();
33832         if(removeCls){
33833             el.removeClass(removeCls);
33834             removeCls = null;
33835         }
33836     };
33837     
33838     return {
33839         /**
33840         * @cfg {Number} minWidth
33841         * The minimum width of the quick tip (defaults to 40)
33842         */
33843        minWidth : 40,
33844         /**
33845         * @cfg {Number} maxWidth
33846         * The maximum width of the quick tip (defaults to 300)
33847         */
33848        maxWidth : 300,
33849         /**
33850         * @cfg {Boolean} interceptTitles
33851         * True to automatically use the element's DOM title value if available (defaults to false)
33852         */
33853        interceptTitles : false,
33854         /**
33855         * @cfg {Boolean} trackMouse
33856         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33857         */
33858        trackMouse : false,
33859         /**
33860         * @cfg {Boolean} hideOnClick
33861         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33862         */
33863        hideOnClick : true,
33864         /**
33865         * @cfg {Number} showDelay
33866         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33867         */
33868        showDelay : 500,
33869         /**
33870         * @cfg {Number} hideDelay
33871         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33872         */
33873        hideDelay : 200,
33874         /**
33875         * @cfg {Boolean} autoHide
33876         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33877         * Used in conjunction with hideDelay.
33878         */
33879        autoHide : true,
33880         /**
33881         * @cfg {Boolean}
33882         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33883         * (defaults to true).  Used in conjunction with autoDismissDelay.
33884         */
33885        autoDismiss : true,
33886         /**
33887         * @cfg {Number}
33888         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33889         */
33890        autoDismissDelay : 5000,
33891        /**
33892         * @cfg {Boolean} animate
33893         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33894         */
33895        animate : false,
33896
33897        /**
33898         * @cfg {String} title
33899         * Title text to display (defaults to '').  This can be any valid HTML markup.
33900         */
33901         title: '',
33902        /**
33903         * @cfg {String} text
33904         * Body text to display (defaults to '').  This can be any valid HTML markup.
33905         */
33906         text : '',
33907        /**
33908         * @cfg {String} cls
33909         * A CSS class to apply to the base quick tip element (defaults to '').
33910         */
33911         cls : '',
33912        /**
33913         * @cfg {Number} width
33914         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33915         * minWidth or maxWidth.
33916         */
33917         width : null,
33918
33919     /**
33920      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33921      * or display QuickTips in a page.
33922      */
33923        init : function(){
33924           tm = Roo.QuickTips;
33925           cfg = tm.tagConfig;
33926           if(!inited){
33927               if(!Roo.isReady){ // allow calling of init() before onReady
33928                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33929                   return;
33930               }
33931               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33932               el.fxDefaults = {stopFx: true};
33933               // maximum custom styling
33934               //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>');
33935               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>');              
33936               tipTitle = el.child('h3');
33937               tipTitle.enableDisplayMode("block");
33938               tipBody = el.child('div.x-tip-bd');
33939               tipBodyText = el.child('div.x-tip-bd-inner');
33940               //bdLeft = el.child('div.x-tip-bd-left');
33941               //bdRight = el.child('div.x-tip-bd-right');
33942               close = el.child('div.x-tip-close');
33943               close.enableDisplayMode("block");
33944               close.on("click", hide);
33945               var d = Roo.get(document);
33946               d.on("mousedown", onDown);
33947               d.on("mouseover", onOver);
33948               d.on("mouseout", onOut);
33949               d.on("mousemove", onMove);
33950               esc = d.addKeyListener(27, hide);
33951               esc.disable();
33952               if(Roo.dd.DD){
33953                   dd = el.initDD("default", null, {
33954                       onDrag : function(){
33955                           el.sync();  
33956                       }
33957                   });
33958                   dd.setHandleElId(tipTitle.id);
33959                   dd.lock();
33960               }
33961               inited = true;
33962           }
33963           this.enable(); 
33964        },
33965
33966     /**
33967      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33968      * are supported:
33969      * <pre>
33970 Property    Type                   Description
33971 ----------  ---------------------  ------------------------------------------------------------------------
33972 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33973      * </ul>
33974      * @param {Object} config The config object
33975      */
33976        register : function(config){
33977            var cs = config instanceof Array ? config : arguments;
33978            for(var i = 0, len = cs.length; i < len; i++) {
33979                var c = cs[i];
33980                var target = c.target;
33981                if(target){
33982                    if(target instanceof Array){
33983                        for(var j = 0, jlen = target.length; j < jlen; j++){
33984                            tagEls[target[j]] = c;
33985                        }
33986                    }else{
33987                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33988                    }
33989                }
33990            }
33991        },
33992
33993     /**
33994      * Removes this quick tip from its element and destroys it.
33995      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33996      */
33997        unregister : function(el){
33998            delete tagEls[Roo.id(el)];
33999        },
34000
34001     /**
34002      * Enable this quick tip.
34003      */
34004        enable : function(){
34005            if(inited && disabled){
34006                locks.pop();
34007                if(locks.length < 1){
34008                    disabled = false;
34009                }
34010            }
34011        },
34012
34013     /**
34014      * Disable this quick tip.
34015      */
34016        disable : function(){
34017           disabled = true;
34018           clearTimeout(showProc);
34019           clearTimeout(hideProc);
34020           clearTimeout(dismissProc);
34021           if(ce){
34022               hide(true);
34023           }
34024           locks.push(1);
34025        },
34026
34027     /**
34028      * Returns true if the quick tip is enabled, else false.
34029      */
34030        isEnabled : function(){
34031             return !disabled;
34032        },
34033
34034         // private
34035        tagConfig : {
34036            namespace : "roo", // was ext?? this may break..
34037            alt_namespace : "ext",
34038            attribute : "qtip",
34039            width : "width",
34040            target : "target",
34041            title : "qtitle",
34042            hide : "hide",
34043            cls : "qclass"
34044        }
34045    };
34046 }();
34047
34048 // backwards compat
34049 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34050  * Based on:
34051  * Ext JS Library 1.1.1
34052  * Copyright(c) 2006-2007, Ext JS, LLC.
34053  *
34054  * Originally Released Under LGPL - original licence link has changed is not relivant.
34055  *
34056  * Fork - LGPL
34057  * <script type="text/javascript">
34058  */
34059  
34060
34061 /**
34062  * @class Roo.tree.TreePanel
34063  * @extends Roo.data.Tree
34064
34065  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34066  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34067  * @cfg {Boolean} enableDD true to enable drag and drop
34068  * @cfg {Boolean} enableDrag true to enable just drag
34069  * @cfg {Boolean} enableDrop true to enable just drop
34070  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34071  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34072  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34073  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34074  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34075  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34076  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34077  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34078  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34079  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34080  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34081  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34082  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34083  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34084  * @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>
34085  * @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>
34086  * 
34087  * @constructor
34088  * @param {String/HTMLElement/Element} el The container element
34089  * @param {Object} config
34090  */
34091 Roo.tree.TreePanel = function(el, config){
34092     var root = false;
34093     var loader = false;
34094     if (config.root) {
34095         root = config.root;
34096         delete config.root;
34097     }
34098     if (config.loader) {
34099         loader = config.loader;
34100         delete config.loader;
34101     }
34102     
34103     Roo.apply(this, config);
34104     Roo.tree.TreePanel.superclass.constructor.call(this);
34105     this.el = Roo.get(el);
34106     this.el.addClass('x-tree');
34107     //console.log(root);
34108     if (root) {
34109         this.setRootNode( Roo.factory(root, Roo.tree));
34110     }
34111     if (loader) {
34112         this.loader = Roo.factory(loader, Roo.tree);
34113     }
34114    /**
34115     * Read-only. The id of the container element becomes this TreePanel's id.
34116     */
34117     this.id = this.el.id;
34118     this.addEvents({
34119         /**
34120         * @event beforeload
34121         * Fires before a node is loaded, return false to cancel
34122         * @param {Node} node The node being loaded
34123         */
34124         "beforeload" : true,
34125         /**
34126         * @event load
34127         * Fires when a node is loaded
34128         * @param {Node} node The node that was loaded
34129         */
34130         "load" : true,
34131         /**
34132         * @event textchange
34133         * Fires when the text for a node is changed
34134         * @param {Node} node The node
34135         * @param {String} text The new text
34136         * @param {String} oldText The old text
34137         */
34138         "textchange" : true,
34139         /**
34140         * @event beforeexpand
34141         * Fires before a node is expanded, return false to cancel.
34142         * @param {Node} node The node
34143         * @param {Boolean} deep
34144         * @param {Boolean} anim
34145         */
34146         "beforeexpand" : true,
34147         /**
34148         * @event beforecollapse
34149         * Fires before a node is collapsed, return false to cancel.
34150         * @param {Node} node The node
34151         * @param {Boolean} deep
34152         * @param {Boolean} anim
34153         */
34154         "beforecollapse" : true,
34155         /**
34156         * @event expand
34157         * Fires when a node is expanded
34158         * @param {Node} node The node
34159         */
34160         "expand" : true,
34161         /**
34162         * @event disabledchange
34163         * Fires when the disabled status of a node changes
34164         * @param {Node} node The node
34165         * @param {Boolean} disabled
34166         */
34167         "disabledchange" : true,
34168         /**
34169         * @event collapse
34170         * Fires when a node is collapsed
34171         * @param {Node} node The node
34172         */
34173         "collapse" : true,
34174         /**
34175         * @event beforeclick
34176         * Fires before click processing on a node. Return false to cancel the default action.
34177         * @param {Node} node The node
34178         * @param {Roo.EventObject} e The event object
34179         */
34180         "beforeclick":true,
34181         /**
34182         * @event checkchange
34183         * Fires when a node with a checkbox's checked property changes
34184         * @param {Node} this This node
34185         * @param {Boolean} checked
34186         */
34187         "checkchange":true,
34188         /**
34189         * @event click
34190         * Fires when a node is clicked
34191         * @param {Node} node The node
34192         * @param {Roo.EventObject} e The event object
34193         */
34194         "click":true,
34195         /**
34196         * @event dblclick
34197         * Fires when a node is double clicked
34198         * @param {Node} node The node
34199         * @param {Roo.EventObject} e The event object
34200         */
34201         "dblclick":true,
34202         /**
34203         * @event contextmenu
34204         * Fires when a node is right clicked
34205         * @param {Node} node The node
34206         * @param {Roo.EventObject} e The event object
34207         */
34208         "contextmenu":true,
34209         /**
34210         * @event beforechildrenrendered
34211         * Fires right before the child nodes for a node are rendered
34212         * @param {Node} node The node
34213         */
34214         "beforechildrenrendered":true,
34215         /**
34216         * @event startdrag
34217         * Fires when a node starts being dragged
34218         * @param {Roo.tree.TreePanel} this
34219         * @param {Roo.tree.TreeNode} node
34220         * @param {event} e The raw browser event
34221         */ 
34222        "startdrag" : true,
34223        /**
34224         * @event enddrag
34225         * Fires when a drag operation is complete
34226         * @param {Roo.tree.TreePanel} this
34227         * @param {Roo.tree.TreeNode} node
34228         * @param {event} e The raw browser event
34229         */
34230        "enddrag" : true,
34231        /**
34232         * @event dragdrop
34233         * Fires when a dragged node is dropped on a valid DD target
34234         * @param {Roo.tree.TreePanel} this
34235         * @param {Roo.tree.TreeNode} node
34236         * @param {DD} dd The dd it was dropped on
34237         * @param {event} e The raw browser event
34238         */
34239        "dragdrop" : true,
34240        /**
34241         * @event beforenodedrop
34242         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34243         * passed to handlers has the following properties:<br />
34244         * <ul style="padding:5px;padding-left:16px;">
34245         * <li>tree - The TreePanel</li>
34246         * <li>target - The node being targeted for the drop</li>
34247         * <li>data - The drag data from the drag source</li>
34248         * <li>point - The point of the drop - append, above or below</li>
34249         * <li>source - The drag source</li>
34250         * <li>rawEvent - Raw mouse event</li>
34251         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34252         * to be inserted by setting them on this object.</li>
34253         * <li>cancel - Set this to true to cancel the drop.</li>
34254         * </ul>
34255         * @param {Object} dropEvent
34256         */
34257        "beforenodedrop" : true,
34258        /**
34259         * @event nodedrop
34260         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34261         * passed to handlers has the following properties:<br />
34262         * <ul style="padding:5px;padding-left:16px;">
34263         * <li>tree - The TreePanel</li>
34264         * <li>target - The node being targeted for the drop</li>
34265         * <li>data - The drag data from the drag source</li>
34266         * <li>point - The point of the drop - append, above or below</li>
34267         * <li>source - The drag source</li>
34268         * <li>rawEvent - Raw mouse event</li>
34269         * <li>dropNode - Dropped node(s).</li>
34270         * </ul>
34271         * @param {Object} dropEvent
34272         */
34273        "nodedrop" : true,
34274         /**
34275         * @event nodedragover
34276         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34277         * passed to handlers has the following properties:<br />
34278         * <ul style="padding:5px;padding-left:16px;">
34279         * <li>tree - The TreePanel</li>
34280         * <li>target - The node being targeted for the drop</li>
34281         * <li>data - The drag data from the drag source</li>
34282         * <li>point - The point of the drop - append, above or below</li>
34283         * <li>source - The drag source</li>
34284         * <li>rawEvent - Raw mouse event</li>
34285         * <li>dropNode - Drop node(s) provided by the source.</li>
34286         * <li>cancel - Set this to true to signal drop not allowed.</li>
34287         * </ul>
34288         * @param {Object} dragOverEvent
34289         */
34290        "nodedragover" : true,
34291        /**
34292         * @event appendnode
34293         * Fires when append node to the tree
34294         * @param {Roo.tree.TreePanel} this
34295         * @param {Roo.tree.TreeNode} node
34296         * @param {Number} index The index of the newly appended node
34297         */
34298        "appendnode" : true
34299         
34300     });
34301     if(this.singleExpand){
34302        this.on("beforeexpand", this.restrictExpand, this);
34303     }
34304     if (this.editor) {
34305         this.editor.tree = this;
34306         this.editor = Roo.factory(this.editor, Roo.tree);
34307     }
34308     
34309     if (this.selModel) {
34310         this.selModel = Roo.factory(this.selModel, Roo.tree);
34311     }
34312    
34313 };
34314 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34315     rootVisible : true,
34316     animate: Roo.enableFx,
34317     lines : true,
34318     enableDD : false,
34319     hlDrop : Roo.enableFx,
34320   
34321     renderer: false,
34322     
34323     rendererTip: false,
34324     // private
34325     restrictExpand : function(node){
34326         var p = node.parentNode;
34327         if(p){
34328             if(p.expandedChild && p.expandedChild.parentNode == p){
34329                 p.expandedChild.collapse();
34330             }
34331             p.expandedChild = node;
34332         }
34333     },
34334
34335     // private override
34336     setRootNode : function(node){
34337         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34338         if(!this.rootVisible){
34339             node.ui = new Roo.tree.RootTreeNodeUI(node);
34340         }
34341         return node;
34342     },
34343
34344     /**
34345      * Returns the container element for this TreePanel
34346      */
34347     getEl : function(){
34348         return this.el;
34349     },
34350
34351     /**
34352      * Returns the default TreeLoader for this TreePanel
34353      */
34354     getLoader : function(){
34355         return this.loader;
34356     },
34357
34358     /**
34359      * Expand all nodes
34360      */
34361     expandAll : function(){
34362         this.root.expand(true);
34363     },
34364
34365     /**
34366      * Collapse all nodes
34367      */
34368     collapseAll : function(){
34369         this.root.collapse(true);
34370     },
34371
34372     /**
34373      * Returns the selection model used by this TreePanel
34374      */
34375     getSelectionModel : function(){
34376         if(!this.selModel){
34377             this.selModel = new Roo.tree.DefaultSelectionModel();
34378         }
34379         return this.selModel;
34380     },
34381
34382     /**
34383      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34384      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34385      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34386      * @return {Array}
34387      */
34388     getChecked : function(a, startNode){
34389         startNode = startNode || this.root;
34390         var r = [];
34391         var f = function(){
34392             if(this.attributes.checked){
34393                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34394             }
34395         }
34396         startNode.cascade(f);
34397         return r;
34398     },
34399
34400     /**
34401      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34402      * @param {String} path
34403      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34404      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34405      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34406      */
34407     expandPath : function(path, attr, callback){
34408         attr = attr || "id";
34409         var keys = path.split(this.pathSeparator);
34410         var curNode = this.root;
34411         if(curNode.attributes[attr] != keys[1]){ // invalid root
34412             if(callback){
34413                 callback(false, null);
34414             }
34415             return;
34416         }
34417         var index = 1;
34418         var f = function(){
34419             if(++index == keys.length){
34420                 if(callback){
34421                     callback(true, curNode);
34422                 }
34423                 return;
34424             }
34425             var c = curNode.findChild(attr, keys[index]);
34426             if(!c){
34427                 if(callback){
34428                     callback(false, curNode);
34429                 }
34430                 return;
34431             }
34432             curNode = c;
34433             c.expand(false, false, f);
34434         };
34435         curNode.expand(false, false, f);
34436     },
34437
34438     /**
34439      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34440      * @param {String} path
34441      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34442      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34443      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34444      */
34445     selectPath : function(path, attr, callback){
34446         attr = attr || "id";
34447         var keys = path.split(this.pathSeparator);
34448         var v = keys.pop();
34449         if(keys.length > 0){
34450             var f = function(success, node){
34451                 if(success && node){
34452                     var n = node.findChild(attr, v);
34453                     if(n){
34454                         n.select();
34455                         if(callback){
34456                             callback(true, n);
34457                         }
34458                     }else if(callback){
34459                         callback(false, n);
34460                     }
34461                 }else{
34462                     if(callback){
34463                         callback(false, n);
34464                     }
34465                 }
34466             };
34467             this.expandPath(keys.join(this.pathSeparator), attr, f);
34468         }else{
34469             this.root.select();
34470             if(callback){
34471                 callback(true, this.root);
34472             }
34473         }
34474     },
34475
34476     getTreeEl : function(){
34477         return this.el;
34478     },
34479
34480     /**
34481      * Trigger rendering of this TreePanel
34482      */
34483     render : function(){
34484         if (this.innerCt) {
34485             return this; // stop it rendering more than once!!
34486         }
34487         
34488         this.innerCt = this.el.createChild({tag:"ul",
34489                cls:"x-tree-root-ct " +
34490                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34491
34492         if(this.containerScroll){
34493             Roo.dd.ScrollManager.register(this.el);
34494         }
34495         if((this.enableDD || this.enableDrop) && !this.dropZone){
34496            /**
34497             * The dropZone used by this tree if drop is enabled
34498             * @type Roo.tree.TreeDropZone
34499             */
34500              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34501                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34502            });
34503         }
34504         if((this.enableDD || this.enableDrag) && !this.dragZone){
34505            /**
34506             * The dragZone used by this tree if drag is enabled
34507             * @type Roo.tree.TreeDragZone
34508             */
34509             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34510                ddGroup: this.ddGroup || "TreeDD",
34511                scroll: this.ddScroll
34512            });
34513         }
34514         this.getSelectionModel().init(this);
34515         if (!this.root) {
34516             Roo.log("ROOT not set in tree");
34517             return this;
34518         }
34519         this.root.render();
34520         if(!this.rootVisible){
34521             this.root.renderChildren();
34522         }
34523         return this;
34524     }
34525 });/*
34526  * Based on:
34527  * Ext JS Library 1.1.1
34528  * Copyright(c) 2006-2007, Ext JS, LLC.
34529  *
34530  * Originally Released Under LGPL - original licence link has changed is not relivant.
34531  *
34532  * Fork - LGPL
34533  * <script type="text/javascript">
34534  */
34535  
34536
34537 /**
34538  * @class Roo.tree.DefaultSelectionModel
34539  * @extends Roo.util.Observable
34540  * The default single selection for a TreePanel.
34541  * @param {Object} cfg Configuration
34542  */
34543 Roo.tree.DefaultSelectionModel = function(cfg){
34544    this.selNode = null;
34545    
34546    
34547    
34548    this.addEvents({
34549        /**
34550         * @event selectionchange
34551         * Fires when the selected node changes
34552         * @param {DefaultSelectionModel} this
34553         * @param {TreeNode} node the new selection
34554         */
34555        "selectionchange" : true,
34556
34557        /**
34558         * @event beforeselect
34559         * Fires before the selected node changes, return false to cancel the change
34560         * @param {DefaultSelectionModel} this
34561         * @param {TreeNode} node the new selection
34562         * @param {TreeNode} node the old selection
34563         */
34564        "beforeselect" : true
34565    });
34566    
34567     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34568 };
34569
34570 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34571     init : function(tree){
34572         this.tree = tree;
34573         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34574         tree.on("click", this.onNodeClick, this);
34575     },
34576     
34577     onNodeClick : function(node, e){
34578         if (e.ctrlKey && this.selNode == node)  {
34579             this.unselect(node);
34580             return;
34581         }
34582         this.select(node);
34583     },
34584     
34585     /**
34586      * Select a node.
34587      * @param {TreeNode} node The node to select
34588      * @return {TreeNode} The selected node
34589      */
34590     select : function(node){
34591         var last = this.selNode;
34592         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34593             if(last){
34594                 last.ui.onSelectedChange(false);
34595             }
34596             this.selNode = node;
34597             node.ui.onSelectedChange(true);
34598             this.fireEvent("selectionchange", this, node, last);
34599         }
34600         return node;
34601     },
34602     
34603     /**
34604      * Deselect a node.
34605      * @param {TreeNode} node The node to unselect
34606      */
34607     unselect : function(node){
34608         if(this.selNode == node){
34609             this.clearSelections();
34610         }    
34611     },
34612     
34613     /**
34614      * Clear all selections
34615      */
34616     clearSelections : function(){
34617         var n = this.selNode;
34618         if(n){
34619             n.ui.onSelectedChange(false);
34620             this.selNode = null;
34621             this.fireEvent("selectionchange", this, null);
34622         }
34623         return n;
34624     },
34625     
34626     /**
34627      * Get the selected node
34628      * @return {TreeNode} The selected node
34629      */
34630     getSelectedNode : function(){
34631         return this.selNode;    
34632     },
34633     
34634     /**
34635      * Returns true if the node is selected
34636      * @param {TreeNode} node The node to check
34637      * @return {Boolean}
34638      */
34639     isSelected : function(node){
34640         return this.selNode == node;  
34641     },
34642
34643     /**
34644      * Selects the node above the selected node in the tree, intelligently walking the nodes
34645      * @return TreeNode The new selection
34646      */
34647     selectPrevious : function(){
34648         var s = this.selNode || this.lastSelNode;
34649         if(!s){
34650             return null;
34651         }
34652         var ps = s.previousSibling;
34653         if(ps){
34654             if(!ps.isExpanded() || ps.childNodes.length < 1){
34655                 return this.select(ps);
34656             } else{
34657                 var lc = ps.lastChild;
34658                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34659                     lc = lc.lastChild;
34660                 }
34661                 return this.select(lc);
34662             }
34663         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34664             return this.select(s.parentNode);
34665         }
34666         return null;
34667     },
34668
34669     /**
34670      * Selects the node above the selected node in the tree, intelligently walking the nodes
34671      * @return TreeNode The new selection
34672      */
34673     selectNext : function(){
34674         var s = this.selNode || this.lastSelNode;
34675         if(!s){
34676             return null;
34677         }
34678         if(s.firstChild && s.isExpanded()){
34679              return this.select(s.firstChild);
34680          }else if(s.nextSibling){
34681              return this.select(s.nextSibling);
34682          }else if(s.parentNode){
34683             var newS = null;
34684             s.parentNode.bubble(function(){
34685                 if(this.nextSibling){
34686                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34687                     return false;
34688                 }
34689             });
34690             return newS;
34691          }
34692         return null;
34693     },
34694
34695     onKeyDown : function(e){
34696         var s = this.selNode || this.lastSelNode;
34697         // undesirable, but required
34698         var sm = this;
34699         if(!s){
34700             return;
34701         }
34702         var k = e.getKey();
34703         switch(k){
34704              case e.DOWN:
34705                  e.stopEvent();
34706                  this.selectNext();
34707              break;
34708              case e.UP:
34709                  e.stopEvent();
34710                  this.selectPrevious();
34711              break;
34712              case e.RIGHT:
34713                  e.preventDefault();
34714                  if(s.hasChildNodes()){
34715                      if(!s.isExpanded()){
34716                          s.expand();
34717                      }else if(s.firstChild){
34718                          this.select(s.firstChild, e);
34719                      }
34720                  }
34721              break;
34722              case e.LEFT:
34723                  e.preventDefault();
34724                  if(s.hasChildNodes() && s.isExpanded()){
34725                      s.collapse();
34726                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34727                      this.select(s.parentNode, e);
34728                  }
34729              break;
34730         };
34731     }
34732 });
34733
34734 /**
34735  * @class Roo.tree.MultiSelectionModel
34736  * @extends Roo.util.Observable
34737  * Multi selection for a TreePanel.
34738  * @param {Object} cfg Configuration
34739  */
34740 Roo.tree.MultiSelectionModel = function(){
34741    this.selNodes = [];
34742    this.selMap = {};
34743    this.addEvents({
34744        /**
34745         * @event selectionchange
34746         * Fires when the selected nodes change
34747         * @param {MultiSelectionModel} this
34748         * @param {Array} nodes Array of the selected nodes
34749         */
34750        "selectionchange" : true
34751    });
34752    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34753    
34754 };
34755
34756 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34757     init : function(tree){
34758         this.tree = tree;
34759         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34760         tree.on("click", this.onNodeClick, this);
34761     },
34762     
34763     onNodeClick : function(node, e){
34764         this.select(node, e, e.ctrlKey);
34765     },
34766     
34767     /**
34768      * Select a node.
34769      * @param {TreeNode} node The node to select
34770      * @param {EventObject} e (optional) An event associated with the selection
34771      * @param {Boolean} keepExisting True to retain existing selections
34772      * @return {TreeNode} The selected node
34773      */
34774     select : function(node, e, keepExisting){
34775         if(keepExisting !== true){
34776             this.clearSelections(true);
34777         }
34778         if(this.isSelected(node)){
34779             this.lastSelNode = node;
34780             return node;
34781         }
34782         this.selNodes.push(node);
34783         this.selMap[node.id] = node;
34784         this.lastSelNode = node;
34785         node.ui.onSelectedChange(true);
34786         this.fireEvent("selectionchange", this, this.selNodes);
34787         return node;
34788     },
34789     
34790     /**
34791      * Deselect a node.
34792      * @param {TreeNode} node The node to unselect
34793      */
34794     unselect : function(node){
34795         if(this.selMap[node.id]){
34796             node.ui.onSelectedChange(false);
34797             var sn = this.selNodes;
34798             var index = -1;
34799             if(sn.indexOf){
34800                 index = sn.indexOf(node);
34801             }else{
34802                 for(var i = 0, len = sn.length; i < len; i++){
34803                     if(sn[i] == node){
34804                         index = i;
34805                         break;
34806                     }
34807                 }
34808             }
34809             if(index != -1){
34810                 this.selNodes.splice(index, 1);
34811             }
34812             delete this.selMap[node.id];
34813             this.fireEvent("selectionchange", this, this.selNodes);
34814         }
34815     },
34816     
34817     /**
34818      * Clear all selections
34819      */
34820     clearSelections : function(suppressEvent){
34821         var sn = this.selNodes;
34822         if(sn.length > 0){
34823             for(var i = 0, len = sn.length; i < len; i++){
34824                 sn[i].ui.onSelectedChange(false);
34825             }
34826             this.selNodes = [];
34827             this.selMap = {};
34828             if(suppressEvent !== true){
34829                 this.fireEvent("selectionchange", this, this.selNodes);
34830             }
34831         }
34832     },
34833     
34834     /**
34835      * Returns true if the node is selected
34836      * @param {TreeNode} node The node to check
34837      * @return {Boolean}
34838      */
34839     isSelected : function(node){
34840         return this.selMap[node.id] ? true : false;  
34841     },
34842     
34843     /**
34844      * Returns an array of the selected nodes
34845      * @return {Array}
34846      */
34847     getSelectedNodes : function(){
34848         return this.selNodes;    
34849     },
34850
34851     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34852
34853     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34854
34855     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34856 });/*
34857  * Based on:
34858  * Ext JS Library 1.1.1
34859  * Copyright(c) 2006-2007, Ext JS, LLC.
34860  *
34861  * Originally Released Under LGPL - original licence link has changed is not relivant.
34862  *
34863  * Fork - LGPL
34864  * <script type="text/javascript">
34865  */
34866  
34867 /**
34868  * @class Roo.tree.TreeNode
34869  * @extends Roo.data.Node
34870  * @cfg {String} text The text for this node
34871  * @cfg {Boolean} expanded true to start the node expanded
34872  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34873  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34874  * @cfg {Boolean} disabled true to start the node disabled
34875  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34876  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34877  * @cfg {String} cls A css class to be added to the node
34878  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34879  * @cfg {String} href URL of the link used for the node (defaults to #)
34880  * @cfg {String} hrefTarget target frame for the link
34881  * @cfg {String} qtip An Ext QuickTip for the node
34882  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34883  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34884  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34885  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34886  * (defaults to undefined with no checkbox rendered)
34887  * @constructor
34888  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34889  */
34890 Roo.tree.TreeNode = function(attributes){
34891     attributes = attributes || {};
34892     if(typeof attributes == "string"){
34893         attributes = {text: attributes};
34894     }
34895     this.childrenRendered = false;
34896     this.rendered = false;
34897     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34898     this.expanded = attributes.expanded === true;
34899     this.isTarget = attributes.isTarget !== false;
34900     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34901     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34902
34903     /**
34904      * Read-only. The text for this node. To change it use setText().
34905      * @type String
34906      */
34907     this.text = attributes.text;
34908     /**
34909      * True if this node is disabled.
34910      * @type Boolean
34911      */
34912     this.disabled = attributes.disabled === true;
34913
34914     this.addEvents({
34915         /**
34916         * @event textchange
34917         * Fires when the text for this node is changed
34918         * @param {Node} this This node
34919         * @param {String} text The new text
34920         * @param {String} oldText The old text
34921         */
34922         "textchange" : true,
34923         /**
34924         * @event beforeexpand
34925         * Fires before this node is expanded, return false to cancel.
34926         * @param {Node} this This node
34927         * @param {Boolean} deep
34928         * @param {Boolean} anim
34929         */
34930         "beforeexpand" : true,
34931         /**
34932         * @event beforecollapse
34933         * Fires before this node is collapsed, return false to cancel.
34934         * @param {Node} this This node
34935         * @param {Boolean} deep
34936         * @param {Boolean} anim
34937         */
34938         "beforecollapse" : true,
34939         /**
34940         * @event expand
34941         * Fires when this node is expanded
34942         * @param {Node} this This node
34943         */
34944         "expand" : true,
34945         /**
34946         * @event disabledchange
34947         * Fires when the disabled status of this node changes
34948         * @param {Node} this This node
34949         * @param {Boolean} disabled
34950         */
34951         "disabledchange" : true,
34952         /**
34953         * @event collapse
34954         * Fires when this node is collapsed
34955         * @param {Node} this This node
34956         */
34957         "collapse" : true,
34958         /**
34959         * @event beforeclick
34960         * Fires before click processing. Return false to cancel the default action.
34961         * @param {Node} this This node
34962         * @param {Roo.EventObject} e The event object
34963         */
34964         "beforeclick":true,
34965         /**
34966         * @event checkchange
34967         * Fires when a node with a checkbox's checked property changes
34968         * @param {Node} this This node
34969         * @param {Boolean} checked
34970         */
34971         "checkchange":true,
34972         /**
34973         * @event click
34974         * Fires when this node is clicked
34975         * @param {Node} this This node
34976         * @param {Roo.EventObject} e The event object
34977         */
34978         "click":true,
34979         /**
34980         * @event dblclick
34981         * Fires when this node is double clicked
34982         * @param {Node} this This node
34983         * @param {Roo.EventObject} e The event object
34984         */
34985         "dblclick":true,
34986         /**
34987         * @event contextmenu
34988         * Fires when this node is right clicked
34989         * @param {Node} this This node
34990         * @param {Roo.EventObject} e The event object
34991         */
34992         "contextmenu":true,
34993         /**
34994         * @event beforechildrenrendered
34995         * Fires right before the child nodes for this node are rendered
34996         * @param {Node} this This node
34997         */
34998         "beforechildrenrendered":true
34999     });
35000
35001     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35002
35003     /**
35004      * Read-only. The UI for this node
35005      * @type TreeNodeUI
35006      */
35007     this.ui = new uiClass(this);
35008     
35009     // finally support items[]
35010     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35011         return;
35012     }
35013     
35014     
35015     Roo.each(this.attributes.items, function(c) {
35016         this.appendChild(Roo.factory(c,Roo.Tree));
35017     }, this);
35018     delete this.attributes.items;
35019     
35020     
35021     
35022 };
35023 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35024     preventHScroll: true,
35025     /**
35026      * Returns true if this node is expanded
35027      * @return {Boolean}
35028      */
35029     isExpanded : function(){
35030         return this.expanded;
35031     },
35032
35033     /**
35034      * Returns the UI object for this node
35035      * @return {TreeNodeUI}
35036      */
35037     getUI : function(){
35038         return this.ui;
35039     },
35040
35041     // private override
35042     setFirstChild : function(node){
35043         var of = this.firstChild;
35044         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35045         if(this.childrenRendered && of && node != of){
35046             of.renderIndent(true, true);
35047         }
35048         if(this.rendered){
35049             this.renderIndent(true, true);
35050         }
35051     },
35052
35053     // private override
35054     setLastChild : function(node){
35055         var ol = this.lastChild;
35056         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35057         if(this.childrenRendered && ol && node != ol){
35058             ol.renderIndent(true, true);
35059         }
35060         if(this.rendered){
35061             this.renderIndent(true, true);
35062         }
35063     },
35064
35065     // these methods are overridden to provide lazy rendering support
35066     // private override
35067     appendChild : function()
35068     {
35069         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35070         if(node && this.childrenRendered){
35071             node.render();
35072         }
35073         this.ui.updateExpandIcon();
35074         return node;
35075     },
35076
35077     // private override
35078     removeChild : function(node){
35079         this.ownerTree.getSelectionModel().unselect(node);
35080         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35081         // if it's been rendered remove dom node
35082         if(this.childrenRendered){
35083             node.ui.remove();
35084         }
35085         if(this.childNodes.length < 1){
35086             this.collapse(false, false);
35087         }else{
35088             this.ui.updateExpandIcon();
35089         }
35090         if(!this.firstChild) {
35091             this.childrenRendered = false;
35092         }
35093         return node;
35094     },
35095
35096     // private override
35097     insertBefore : function(node, refNode){
35098         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35099         if(newNode && refNode && this.childrenRendered){
35100             node.render();
35101         }
35102         this.ui.updateExpandIcon();
35103         return newNode;
35104     },
35105
35106     /**
35107      * Sets the text for this node
35108      * @param {String} text
35109      */
35110     setText : function(text){
35111         var oldText = this.text;
35112         this.text = text;
35113         this.attributes.text = text;
35114         if(this.rendered){ // event without subscribing
35115             this.ui.onTextChange(this, text, oldText);
35116         }
35117         this.fireEvent("textchange", this, text, oldText);
35118     },
35119
35120     /**
35121      * Triggers selection of this node
35122      */
35123     select : function(){
35124         this.getOwnerTree().getSelectionModel().select(this);
35125     },
35126
35127     /**
35128      * Triggers deselection of this node
35129      */
35130     unselect : function(){
35131         this.getOwnerTree().getSelectionModel().unselect(this);
35132     },
35133
35134     /**
35135      * Returns true if this node is selected
35136      * @return {Boolean}
35137      */
35138     isSelected : function(){
35139         return this.getOwnerTree().getSelectionModel().isSelected(this);
35140     },
35141
35142     /**
35143      * Expand this node.
35144      * @param {Boolean} deep (optional) True to expand all children as well
35145      * @param {Boolean} anim (optional) false to cancel the default animation
35146      * @param {Function} callback (optional) A callback to be called when
35147      * expanding this node completes (does not wait for deep expand to complete).
35148      * Called with 1 parameter, this node.
35149      */
35150     expand : function(deep, anim, callback){
35151         if(!this.expanded){
35152             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35153                 return;
35154             }
35155             if(!this.childrenRendered){
35156                 this.renderChildren();
35157             }
35158             this.expanded = true;
35159             
35160             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35161                 this.ui.animExpand(function(){
35162                     this.fireEvent("expand", this);
35163                     if(typeof callback == "function"){
35164                         callback(this);
35165                     }
35166                     if(deep === true){
35167                         this.expandChildNodes(true);
35168                     }
35169                 }.createDelegate(this));
35170                 return;
35171             }else{
35172                 this.ui.expand();
35173                 this.fireEvent("expand", this);
35174                 if(typeof callback == "function"){
35175                     callback(this);
35176                 }
35177             }
35178         }else{
35179            if(typeof callback == "function"){
35180                callback(this);
35181            }
35182         }
35183         if(deep === true){
35184             this.expandChildNodes(true);
35185         }
35186     },
35187
35188     isHiddenRoot : function(){
35189         return this.isRoot && !this.getOwnerTree().rootVisible;
35190     },
35191
35192     /**
35193      * Collapse this node.
35194      * @param {Boolean} deep (optional) True to collapse all children as well
35195      * @param {Boolean} anim (optional) false to cancel the default animation
35196      */
35197     collapse : function(deep, anim){
35198         if(this.expanded && !this.isHiddenRoot()){
35199             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35200                 return;
35201             }
35202             this.expanded = false;
35203             if((this.getOwnerTree().animate && anim !== false) || anim){
35204                 this.ui.animCollapse(function(){
35205                     this.fireEvent("collapse", this);
35206                     if(deep === true){
35207                         this.collapseChildNodes(true);
35208                     }
35209                 }.createDelegate(this));
35210                 return;
35211             }else{
35212                 this.ui.collapse();
35213                 this.fireEvent("collapse", this);
35214             }
35215         }
35216         if(deep === true){
35217             var cs = this.childNodes;
35218             for(var i = 0, len = cs.length; i < len; i++) {
35219                 cs[i].collapse(true, false);
35220             }
35221         }
35222     },
35223
35224     // private
35225     delayedExpand : function(delay){
35226         if(!this.expandProcId){
35227             this.expandProcId = this.expand.defer(delay, this);
35228         }
35229     },
35230
35231     // private
35232     cancelExpand : function(){
35233         if(this.expandProcId){
35234             clearTimeout(this.expandProcId);
35235         }
35236         this.expandProcId = false;
35237     },
35238
35239     /**
35240      * Toggles expanded/collapsed state of the node
35241      */
35242     toggle : function(){
35243         if(this.expanded){
35244             this.collapse();
35245         }else{
35246             this.expand();
35247         }
35248     },
35249
35250     /**
35251      * Ensures all parent nodes are expanded
35252      */
35253     ensureVisible : function(callback){
35254         var tree = this.getOwnerTree();
35255         tree.expandPath(this.parentNode.getPath(), false, function(){
35256             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35257             Roo.callback(callback);
35258         }.createDelegate(this));
35259     },
35260
35261     /**
35262      * Expand all child nodes
35263      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35264      */
35265     expandChildNodes : function(deep){
35266         var cs = this.childNodes;
35267         for(var i = 0, len = cs.length; i < len; i++) {
35268                 cs[i].expand(deep);
35269         }
35270     },
35271
35272     /**
35273      * Collapse all child nodes
35274      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35275      */
35276     collapseChildNodes : function(deep){
35277         var cs = this.childNodes;
35278         for(var i = 0, len = cs.length; i < len; i++) {
35279                 cs[i].collapse(deep);
35280         }
35281     },
35282
35283     /**
35284      * Disables this node
35285      */
35286     disable : function(){
35287         this.disabled = true;
35288         this.unselect();
35289         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35290             this.ui.onDisableChange(this, true);
35291         }
35292         this.fireEvent("disabledchange", this, true);
35293     },
35294
35295     /**
35296      * Enables this node
35297      */
35298     enable : function(){
35299         this.disabled = false;
35300         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35301             this.ui.onDisableChange(this, false);
35302         }
35303         this.fireEvent("disabledchange", this, false);
35304     },
35305
35306     // private
35307     renderChildren : function(suppressEvent){
35308         if(suppressEvent !== false){
35309             this.fireEvent("beforechildrenrendered", this);
35310         }
35311         var cs = this.childNodes;
35312         for(var i = 0, len = cs.length; i < len; i++){
35313             cs[i].render(true);
35314         }
35315         this.childrenRendered = true;
35316     },
35317
35318     // private
35319     sort : function(fn, scope){
35320         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35321         if(this.childrenRendered){
35322             var cs = this.childNodes;
35323             for(var i = 0, len = cs.length; i < len; i++){
35324                 cs[i].render(true);
35325             }
35326         }
35327     },
35328
35329     // private
35330     render : function(bulkRender){
35331         this.ui.render(bulkRender);
35332         if(!this.rendered){
35333             this.rendered = true;
35334             if(this.expanded){
35335                 this.expanded = false;
35336                 this.expand(false, false);
35337             }
35338         }
35339     },
35340
35341     // private
35342     renderIndent : function(deep, refresh){
35343         if(refresh){
35344             this.ui.childIndent = null;
35345         }
35346         this.ui.renderIndent();
35347         if(deep === true && this.childrenRendered){
35348             var cs = this.childNodes;
35349             for(var i = 0, len = cs.length; i < len; i++){
35350                 cs[i].renderIndent(true, refresh);
35351             }
35352         }
35353     }
35354 });/*
35355  * Based on:
35356  * Ext JS Library 1.1.1
35357  * Copyright(c) 2006-2007, Ext JS, LLC.
35358  *
35359  * Originally Released Under LGPL - original licence link has changed is not relivant.
35360  *
35361  * Fork - LGPL
35362  * <script type="text/javascript">
35363  */
35364  
35365 /**
35366  * @class Roo.tree.AsyncTreeNode
35367  * @extends Roo.tree.TreeNode
35368  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35369  * @constructor
35370  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35371  */
35372  Roo.tree.AsyncTreeNode = function(config){
35373     this.loaded = false;
35374     this.loading = false;
35375     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35376     /**
35377     * @event beforeload
35378     * Fires before this node is loaded, return false to cancel
35379     * @param {Node} this This node
35380     */
35381     this.addEvents({'beforeload':true, 'load': true});
35382     /**
35383     * @event load
35384     * Fires when this node is loaded
35385     * @param {Node} this This node
35386     */
35387     /**
35388      * The loader used by this node (defaults to using the tree's defined loader)
35389      * @type TreeLoader
35390      * @property loader
35391      */
35392 };
35393 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35394     expand : function(deep, anim, callback){
35395         if(this.loading){ // if an async load is already running, waiting til it's done
35396             var timer;
35397             var f = function(){
35398                 if(!this.loading){ // done loading
35399                     clearInterval(timer);
35400                     this.expand(deep, anim, callback);
35401                 }
35402             }.createDelegate(this);
35403             timer = setInterval(f, 200);
35404             return;
35405         }
35406         if(!this.loaded){
35407             if(this.fireEvent("beforeload", this) === false){
35408                 return;
35409             }
35410             this.loading = true;
35411             this.ui.beforeLoad(this);
35412             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35413             if(loader){
35414                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35415                 return;
35416             }
35417         }
35418         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35419     },
35420     
35421     /**
35422      * Returns true if this node is currently loading
35423      * @return {Boolean}
35424      */
35425     isLoading : function(){
35426         return this.loading;  
35427     },
35428     
35429     loadComplete : function(deep, anim, callback){
35430         this.loading = false;
35431         this.loaded = true;
35432         this.ui.afterLoad(this);
35433         this.fireEvent("load", this);
35434         this.expand(deep, anim, callback);
35435     },
35436     
35437     /**
35438      * Returns true if this node has been loaded
35439      * @return {Boolean}
35440      */
35441     isLoaded : function(){
35442         return this.loaded;
35443     },
35444     
35445     hasChildNodes : function(){
35446         if(!this.isLeaf() && !this.loaded){
35447             return true;
35448         }else{
35449             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35450         }
35451     },
35452
35453     /**
35454      * Trigger a reload for this node
35455      * @param {Function} callback
35456      */
35457     reload : function(callback){
35458         this.collapse(false, false);
35459         while(this.firstChild){
35460             this.removeChild(this.firstChild);
35461         }
35462         this.childrenRendered = false;
35463         this.loaded = false;
35464         if(this.isHiddenRoot()){
35465             this.expanded = false;
35466         }
35467         this.expand(false, false, callback);
35468     }
35469 });/*
35470  * Based on:
35471  * Ext JS Library 1.1.1
35472  * Copyright(c) 2006-2007, Ext JS, LLC.
35473  *
35474  * Originally Released Under LGPL - original licence link has changed is not relivant.
35475  *
35476  * Fork - LGPL
35477  * <script type="text/javascript">
35478  */
35479  
35480 /**
35481  * @class Roo.tree.TreeNodeUI
35482  * @constructor
35483  * @param {Object} node The node to render
35484  * The TreeNode UI implementation is separate from the
35485  * tree implementation. Unless you are customizing the tree UI,
35486  * you should never have to use this directly.
35487  */
35488 Roo.tree.TreeNodeUI = function(node){
35489     this.node = node;
35490     this.rendered = false;
35491     this.animating = false;
35492     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35493 };
35494
35495 Roo.tree.TreeNodeUI.prototype = {
35496     removeChild : function(node){
35497         if(this.rendered){
35498             this.ctNode.removeChild(node.ui.getEl());
35499         }
35500     },
35501
35502     beforeLoad : function(){
35503          this.addClass("x-tree-node-loading");
35504     },
35505
35506     afterLoad : function(){
35507          this.removeClass("x-tree-node-loading");
35508     },
35509
35510     onTextChange : function(node, text, oldText){
35511         if(this.rendered){
35512             this.textNode.innerHTML = text;
35513         }
35514     },
35515
35516     onDisableChange : function(node, state){
35517         this.disabled = state;
35518         if(state){
35519             this.addClass("x-tree-node-disabled");
35520         }else{
35521             this.removeClass("x-tree-node-disabled");
35522         }
35523     },
35524
35525     onSelectedChange : function(state){
35526         if(state){
35527             this.focus();
35528             this.addClass("x-tree-selected");
35529         }else{
35530             //this.blur();
35531             this.removeClass("x-tree-selected");
35532         }
35533     },
35534
35535     onMove : function(tree, node, oldParent, newParent, index, refNode){
35536         this.childIndent = null;
35537         if(this.rendered){
35538             var targetNode = newParent.ui.getContainer();
35539             if(!targetNode){//target not rendered
35540                 this.holder = document.createElement("div");
35541                 this.holder.appendChild(this.wrap);
35542                 return;
35543             }
35544             var insertBefore = refNode ? refNode.ui.getEl() : null;
35545             if(insertBefore){
35546                 targetNode.insertBefore(this.wrap, insertBefore);
35547             }else{
35548                 targetNode.appendChild(this.wrap);
35549             }
35550             this.node.renderIndent(true);
35551         }
35552     },
35553
35554     addClass : function(cls){
35555         if(this.elNode){
35556             Roo.fly(this.elNode).addClass(cls);
35557         }
35558     },
35559
35560     removeClass : function(cls){
35561         if(this.elNode){
35562             Roo.fly(this.elNode).removeClass(cls);
35563         }
35564     },
35565
35566     remove : function(){
35567         if(this.rendered){
35568             this.holder = document.createElement("div");
35569             this.holder.appendChild(this.wrap);
35570         }
35571     },
35572
35573     fireEvent : function(){
35574         return this.node.fireEvent.apply(this.node, arguments);
35575     },
35576
35577     initEvents : function(){
35578         this.node.on("move", this.onMove, this);
35579         var E = Roo.EventManager;
35580         var a = this.anchor;
35581
35582         var el = Roo.fly(a, '_treeui');
35583
35584         if(Roo.isOpera){ // opera render bug ignores the CSS
35585             el.setStyle("text-decoration", "none");
35586         }
35587
35588         el.on("click", this.onClick, this);
35589         el.on("dblclick", this.onDblClick, this);
35590
35591         if(this.checkbox){
35592             Roo.EventManager.on(this.checkbox,
35593                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35594         }
35595
35596         el.on("contextmenu", this.onContextMenu, this);
35597
35598         var icon = Roo.fly(this.iconNode);
35599         icon.on("click", this.onClick, this);
35600         icon.on("dblclick", this.onDblClick, this);
35601         icon.on("contextmenu", this.onContextMenu, this);
35602         E.on(this.ecNode, "click", this.ecClick, this, true);
35603
35604         if(this.node.disabled){
35605             this.addClass("x-tree-node-disabled");
35606         }
35607         if(this.node.hidden){
35608             this.addClass("x-tree-node-disabled");
35609         }
35610         var ot = this.node.getOwnerTree();
35611         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35612         if(dd && (!this.node.isRoot || ot.rootVisible)){
35613             Roo.dd.Registry.register(this.elNode, {
35614                 node: this.node,
35615                 handles: this.getDDHandles(),
35616                 isHandle: false
35617             });
35618         }
35619     },
35620
35621     getDDHandles : function(){
35622         return [this.iconNode, this.textNode];
35623     },
35624
35625     hide : function(){
35626         if(this.rendered){
35627             this.wrap.style.display = "none";
35628         }
35629     },
35630
35631     show : function(){
35632         if(this.rendered){
35633             this.wrap.style.display = "";
35634         }
35635     },
35636
35637     onContextMenu : function(e){
35638         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35639             e.preventDefault();
35640             this.focus();
35641             this.fireEvent("contextmenu", this.node, e);
35642         }
35643     },
35644
35645     onClick : function(e){
35646         if(this.dropping){
35647             e.stopEvent();
35648             return;
35649         }
35650         if(this.fireEvent("beforeclick", this.node, e) !== false){
35651             if(!this.disabled && this.node.attributes.href){
35652                 this.fireEvent("click", this.node, e);
35653                 return;
35654             }
35655             e.preventDefault();
35656             if(this.disabled){
35657                 return;
35658             }
35659
35660             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35661                 this.node.toggle();
35662             }
35663
35664             this.fireEvent("click", this.node, e);
35665         }else{
35666             e.stopEvent();
35667         }
35668     },
35669
35670     onDblClick : function(e){
35671         e.preventDefault();
35672         if(this.disabled){
35673             return;
35674         }
35675         if(this.checkbox){
35676             this.toggleCheck();
35677         }
35678         if(!this.animating && this.node.hasChildNodes()){
35679             this.node.toggle();
35680         }
35681         this.fireEvent("dblclick", this.node, e);
35682     },
35683
35684     onCheckChange : function(){
35685         var checked = this.checkbox.checked;
35686         this.node.attributes.checked = checked;
35687         this.fireEvent('checkchange', this.node, checked);
35688     },
35689
35690     ecClick : function(e){
35691         if(!this.animating && this.node.hasChildNodes()){
35692             this.node.toggle();
35693         }
35694     },
35695
35696     startDrop : function(){
35697         this.dropping = true;
35698     },
35699
35700     // delayed drop so the click event doesn't get fired on a drop
35701     endDrop : function(){
35702        setTimeout(function(){
35703            this.dropping = false;
35704        }.createDelegate(this), 50);
35705     },
35706
35707     expand : function(){
35708         this.updateExpandIcon();
35709         this.ctNode.style.display = "";
35710     },
35711
35712     focus : function(){
35713         if(!this.node.preventHScroll){
35714             try{this.anchor.focus();
35715             }catch(e){}
35716         }else if(!Roo.isIE){
35717             try{
35718                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35719                 var l = noscroll.scrollLeft;
35720                 this.anchor.focus();
35721                 noscroll.scrollLeft = l;
35722             }catch(e){}
35723         }
35724     },
35725
35726     toggleCheck : function(value){
35727         var cb = this.checkbox;
35728         if(cb){
35729             cb.checked = (value === undefined ? !cb.checked : value);
35730         }
35731     },
35732
35733     blur : function(){
35734         try{
35735             this.anchor.blur();
35736         }catch(e){}
35737     },
35738
35739     animExpand : function(callback){
35740         var ct = Roo.get(this.ctNode);
35741         ct.stopFx();
35742         if(!this.node.hasChildNodes()){
35743             this.updateExpandIcon();
35744             this.ctNode.style.display = "";
35745             Roo.callback(callback);
35746             return;
35747         }
35748         this.animating = true;
35749         this.updateExpandIcon();
35750
35751         ct.slideIn('t', {
35752            callback : function(){
35753                this.animating = false;
35754                Roo.callback(callback);
35755             },
35756             scope: this,
35757             duration: this.node.ownerTree.duration || .25
35758         });
35759     },
35760
35761     highlight : function(){
35762         var tree = this.node.getOwnerTree();
35763         Roo.fly(this.wrap).highlight(
35764             tree.hlColor || "C3DAF9",
35765             {endColor: tree.hlBaseColor}
35766         );
35767     },
35768
35769     collapse : function(){
35770         this.updateExpandIcon();
35771         this.ctNode.style.display = "none";
35772     },
35773
35774     animCollapse : function(callback){
35775         var ct = Roo.get(this.ctNode);
35776         ct.enableDisplayMode('block');
35777         ct.stopFx();
35778
35779         this.animating = true;
35780         this.updateExpandIcon();
35781
35782         ct.slideOut('t', {
35783             callback : function(){
35784                this.animating = false;
35785                Roo.callback(callback);
35786             },
35787             scope: this,
35788             duration: this.node.ownerTree.duration || .25
35789         });
35790     },
35791
35792     getContainer : function(){
35793         return this.ctNode;
35794     },
35795
35796     getEl : function(){
35797         return this.wrap;
35798     },
35799
35800     appendDDGhost : function(ghostNode){
35801         ghostNode.appendChild(this.elNode.cloneNode(true));
35802     },
35803
35804     getDDRepairXY : function(){
35805         return Roo.lib.Dom.getXY(this.iconNode);
35806     },
35807
35808     onRender : function(){
35809         this.render();
35810     },
35811
35812     render : function(bulkRender){
35813         var n = this.node, a = n.attributes;
35814         var targetNode = n.parentNode ?
35815               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35816
35817         if(!this.rendered){
35818             this.rendered = true;
35819
35820             this.renderElements(n, a, targetNode, bulkRender);
35821
35822             if(a.qtip){
35823                if(this.textNode.setAttributeNS){
35824                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35825                    if(a.qtipTitle){
35826                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35827                    }
35828                }else{
35829                    this.textNode.setAttribute("ext:qtip", a.qtip);
35830                    if(a.qtipTitle){
35831                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35832                    }
35833                }
35834             }else if(a.qtipCfg){
35835                 a.qtipCfg.target = Roo.id(this.textNode);
35836                 Roo.QuickTips.register(a.qtipCfg);
35837             }
35838             this.initEvents();
35839             if(!this.node.expanded){
35840                 this.updateExpandIcon();
35841             }
35842         }else{
35843             if(bulkRender === true) {
35844                 targetNode.appendChild(this.wrap);
35845             }
35846         }
35847     },
35848
35849     renderElements : function(n, a, targetNode, bulkRender)
35850     {
35851         // add some indent caching, this helps performance when rendering a large tree
35852         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35853         var t = n.getOwnerTree();
35854         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35855         if (typeof(n.attributes.html) != 'undefined') {
35856             txt = n.attributes.html;
35857         }
35858         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35859         var cb = typeof a.checked == 'boolean';
35860         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35861         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35862             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35863             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35864             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35865             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35866             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35867              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35868                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35869             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35870             "</li>"];
35871
35872         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35873             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35874                                 n.nextSibling.ui.getEl(), buf.join(""));
35875         }else{
35876             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35877         }
35878
35879         this.elNode = this.wrap.childNodes[0];
35880         this.ctNode = this.wrap.childNodes[1];
35881         var cs = this.elNode.childNodes;
35882         this.indentNode = cs[0];
35883         this.ecNode = cs[1];
35884         this.iconNode = cs[2];
35885         var index = 3;
35886         if(cb){
35887             this.checkbox = cs[3];
35888             index++;
35889         }
35890         this.anchor = cs[index];
35891         this.textNode = cs[index].firstChild;
35892     },
35893
35894     getAnchor : function(){
35895         return this.anchor;
35896     },
35897
35898     getTextEl : function(){
35899         return this.textNode;
35900     },
35901
35902     getIconEl : function(){
35903         return this.iconNode;
35904     },
35905
35906     isChecked : function(){
35907         return this.checkbox ? this.checkbox.checked : false;
35908     },
35909
35910     updateExpandIcon : function(){
35911         if(this.rendered){
35912             var n = this.node, c1, c2;
35913             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35914             var hasChild = n.hasChildNodes();
35915             if(hasChild){
35916                 if(n.expanded){
35917                     cls += "-minus";
35918                     c1 = "x-tree-node-collapsed";
35919                     c2 = "x-tree-node-expanded";
35920                 }else{
35921                     cls += "-plus";
35922                     c1 = "x-tree-node-expanded";
35923                     c2 = "x-tree-node-collapsed";
35924                 }
35925                 if(this.wasLeaf){
35926                     this.removeClass("x-tree-node-leaf");
35927                     this.wasLeaf = false;
35928                 }
35929                 if(this.c1 != c1 || this.c2 != c2){
35930                     Roo.fly(this.elNode).replaceClass(c1, c2);
35931                     this.c1 = c1; this.c2 = c2;
35932                 }
35933             }else{
35934                 // this changes non-leafs into leafs if they have no children.
35935                 // it's not very rational behaviour..
35936                 
35937                 if(!this.wasLeaf && this.node.leaf){
35938                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35939                     delete this.c1;
35940                     delete this.c2;
35941                     this.wasLeaf = true;
35942                 }
35943             }
35944             var ecc = "x-tree-ec-icon "+cls;
35945             if(this.ecc != ecc){
35946                 this.ecNode.className = ecc;
35947                 this.ecc = ecc;
35948             }
35949         }
35950     },
35951
35952     getChildIndent : function(){
35953         if(!this.childIndent){
35954             var buf = [];
35955             var p = this.node;
35956             while(p){
35957                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35958                     if(!p.isLast()) {
35959                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35960                     } else {
35961                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35962                     }
35963                 }
35964                 p = p.parentNode;
35965             }
35966             this.childIndent = buf.join("");
35967         }
35968         return this.childIndent;
35969     },
35970
35971     renderIndent : function(){
35972         if(this.rendered){
35973             var indent = "";
35974             var p = this.node.parentNode;
35975             if(p){
35976                 indent = p.ui.getChildIndent();
35977             }
35978             if(this.indentMarkup != indent){ // don't rerender if not required
35979                 this.indentNode.innerHTML = indent;
35980                 this.indentMarkup = indent;
35981             }
35982             this.updateExpandIcon();
35983         }
35984     }
35985 };
35986
35987 Roo.tree.RootTreeNodeUI = function(){
35988     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35989 };
35990 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35991     render : function(){
35992         if(!this.rendered){
35993             var targetNode = this.node.ownerTree.innerCt.dom;
35994             this.node.expanded = true;
35995             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35996             this.wrap = this.ctNode = targetNode.firstChild;
35997         }
35998     },
35999     collapse : function(){
36000     },
36001     expand : function(){
36002     }
36003 });/*
36004  * Based on:
36005  * Ext JS Library 1.1.1
36006  * Copyright(c) 2006-2007, Ext JS, LLC.
36007  *
36008  * Originally Released Under LGPL - original licence link has changed is not relivant.
36009  *
36010  * Fork - LGPL
36011  * <script type="text/javascript">
36012  */
36013 /**
36014  * @class Roo.tree.TreeLoader
36015  * @extends Roo.util.Observable
36016  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36017  * nodes from a specified URL. The response must be a javascript Array definition
36018  * who's elements are node definition objects. eg:
36019  * <pre><code>
36020 {  success : true,
36021    data :      [
36022    
36023     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36024     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36025     ]
36026 }
36027
36028
36029 </code></pre>
36030  * <br><br>
36031  * The old style respose with just an array is still supported, but not recommended.
36032  * <br><br>
36033  *
36034  * A server request is sent, and child nodes are loaded only when a node is expanded.
36035  * The loading node's id is passed to the server under the parameter name "node" to
36036  * enable the server to produce the correct child nodes.
36037  * <br><br>
36038  * To pass extra parameters, an event handler may be attached to the "beforeload"
36039  * event, and the parameters specified in the TreeLoader's baseParams property:
36040  * <pre><code>
36041     myTreeLoader.on("beforeload", function(treeLoader, node) {
36042         this.baseParams.category = node.attributes.category;
36043     }, this);
36044     
36045 </code></pre>
36046  *
36047  * This would pass an HTTP parameter called "category" to the server containing
36048  * the value of the Node's "category" attribute.
36049  * @constructor
36050  * Creates a new Treeloader.
36051  * @param {Object} config A config object containing config properties.
36052  */
36053 Roo.tree.TreeLoader = function(config){
36054     this.baseParams = {};
36055     this.requestMethod = "POST";
36056     Roo.apply(this, config);
36057
36058     this.addEvents({
36059     
36060         /**
36061          * @event beforeload
36062          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36063          * @param {Object} This TreeLoader object.
36064          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36065          * @param {Object} callback The callback function specified in the {@link #load} call.
36066          */
36067         beforeload : true,
36068         /**
36069          * @event load
36070          * Fires when the node has been successfuly loaded.
36071          * @param {Object} This TreeLoader object.
36072          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36073          * @param {Object} response The response object containing the data from the server.
36074          */
36075         load : true,
36076         /**
36077          * @event loadexception
36078          * Fires if the network request failed.
36079          * @param {Object} This TreeLoader object.
36080          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36081          * @param {Object} response The response object containing the data from the server.
36082          */
36083         loadexception : true,
36084         /**
36085          * @event create
36086          * Fires before a node is created, enabling you to return custom Node types 
36087          * @param {Object} This TreeLoader object.
36088          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36089          */
36090         create : true
36091     });
36092
36093     Roo.tree.TreeLoader.superclass.constructor.call(this);
36094 };
36095
36096 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36097     /**
36098     * @cfg {String} dataUrl The URL from which to request a Json string which
36099     * specifies an array of node definition object representing the child nodes
36100     * to be loaded.
36101     */
36102     /**
36103     * @cfg {String} requestMethod either GET or POST
36104     * defaults to POST (due to BC)
36105     * to be loaded.
36106     */
36107     /**
36108     * @cfg {Object} baseParams (optional) An object containing properties which
36109     * specify HTTP parameters to be passed to each request for child nodes.
36110     */
36111     /**
36112     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36113     * created by this loader. If the attributes sent by the server have an attribute in this object,
36114     * they take priority.
36115     */
36116     /**
36117     * @cfg {Object} uiProviders (optional) An object containing properties which
36118     * 
36119     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36120     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36121     * <i>uiProvider</i> attribute of a returned child node is a string rather
36122     * than a reference to a TreeNodeUI implementation, this that string value
36123     * is used as a property name in the uiProviders object. You can define the provider named
36124     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36125     */
36126     uiProviders : {},
36127
36128     /**
36129     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36130     * child nodes before loading.
36131     */
36132     clearOnLoad : true,
36133
36134     /**
36135     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36136     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36137     * Grid query { data : [ .....] }
36138     */
36139     
36140     root : false,
36141      /**
36142     * @cfg {String} queryParam (optional) 
36143     * Name of the query as it will be passed on the querystring (defaults to 'node')
36144     * eg. the request will be ?node=[id]
36145     */
36146     
36147     
36148     queryParam: false,
36149     
36150     /**
36151      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36152      * This is called automatically when a node is expanded, but may be used to reload
36153      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36154      * @param {Roo.tree.TreeNode} node
36155      * @param {Function} callback
36156      */
36157     load : function(node, callback){
36158         if(this.clearOnLoad){
36159             while(node.firstChild){
36160                 node.removeChild(node.firstChild);
36161             }
36162         }
36163         if(node.attributes.children){ // preloaded json children
36164             var cs = node.attributes.children;
36165             for(var i = 0, len = cs.length; i < len; i++){
36166                 node.appendChild(this.createNode(cs[i]));
36167             }
36168             if(typeof callback == "function"){
36169                 callback();
36170             }
36171         }else if(this.dataUrl){
36172             this.requestData(node, callback);
36173         }
36174     },
36175
36176     getParams: function(node){
36177         var buf = [], bp = this.baseParams;
36178         for(var key in bp){
36179             if(typeof bp[key] != "function"){
36180                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36181             }
36182         }
36183         var n = this.queryParam === false ? 'node' : this.queryParam;
36184         buf.push(n + "=", encodeURIComponent(node.id));
36185         return buf.join("");
36186     },
36187
36188     requestData : function(node, callback){
36189         if(this.fireEvent("beforeload", this, node, callback) !== false){
36190             this.transId = Roo.Ajax.request({
36191                 method:this.requestMethod,
36192                 url: this.dataUrl||this.url,
36193                 success: this.handleResponse,
36194                 failure: this.handleFailure,
36195                 scope: this,
36196                 argument: {callback: callback, node: node},
36197                 params: this.getParams(node)
36198             });
36199         }else{
36200             // if the load is cancelled, make sure we notify
36201             // the node that we are done
36202             if(typeof callback == "function"){
36203                 callback();
36204             }
36205         }
36206     },
36207
36208     isLoading : function(){
36209         return this.transId ? true : false;
36210     },
36211
36212     abort : function(){
36213         if(this.isLoading()){
36214             Roo.Ajax.abort(this.transId);
36215         }
36216     },
36217
36218     // private
36219     createNode : function(attr)
36220     {
36221         // apply baseAttrs, nice idea Corey!
36222         if(this.baseAttrs){
36223             Roo.applyIf(attr, this.baseAttrs);
36224         }
36225         if(this.applyLoader !== false){
36226             attr.loader = this;
36227         }
36228         // uiProvider = depreciated..
36229         
36230         if(typeof(attr.uiProvider) == 'string'){
36231            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36232                 /**  eval:var:attr */ eval(attr.uiProvider);
36233         }
36234         if(typeof(this.uiProviders['default']) != 'undefined') {
36235             attr.uiProvider = this.uiProviders['default'];
36236         }
36237         
36238         this.fireEvent('create', this, attr);
36239         
36240         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36241         return(attr.leaf ?
36242                         new Roo.tree.TreeNode(attr) :
36243                         new Roo.tree.AsyncTreeNode(attr));
36244     },
36245
36246     processResponse : function(response, node, callback)
36247     {
36248         var json = response.responseText;
36249         try {
36250             
36251             var o = Roo.decode(json);
36252             
36253             if (this.root === false && typeof(o.success) != undefined) {
36254                 this.root = 'data'; // the default behaviour for list like data..
36255                 }
36256                 
36257             if (this.root !== false &&  !o.success) {
36258                 // it's a failure condition.
36259                 var a = response.argument;
36260                 this.fireEvent("loadexception", this, a.node, response);
36261                 Roo.log("Load failed - should have a handler really");
36262                 return;
36263             }
36264             
36265             
36266             
36267             if (this.root !== false) {
36268                  o = o[this.root];
36269             }
36270             
36271             for(var i = 0, len = o.length; i < len; i++){
36272                 var n = this.createNode(o[i]);
36273                 if(n){
36274                     node.appendChild(n);
36275                 }
36276             }
36277             if(typeof callback == "function"){
36278                 callback(this, node);
36279             }
36280         }catch(e){
36281             this.handleFailure(response);
36282         }
36283     },
36284
36285     handleResponse : function(response){
36286         this.transId = false;
36287         var a = response.argument;
36288         this.processResponse(response, a.node, a.callback);
36289         this.fireEvent("load", this, a.node, response);
36290     },
36291
36292     handleFailure : function(response)
36293     {
36294         // should handle failure better..
36295         this.transId = false;
36296         var a = response.argument;
36297         this.fireEvent("loadexception", this, a.node, response);
36298         if(typeof a.callback == "function"){
36299             a.callback(this, a.node);
36300         }
36301     }
36302 });/*
36303  * Based on:
36304  * Ext JS Library 1.1.1
36305  * Copyright(c) 2006-2007, Ext JS, LLC.
36306  *
36307  * Originally Released Under LGPL - original licence link has changed is not relivant.
36308  *
36309  * Fork - LGPL
36310  * <script type="text/javascript">
36311  */
36312
36313 /**
36314 * @class Roo.tree.TreeFilter
36315 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36316 * @param {TreePanel} tree
36317 * @param {Object} config (optional)
36318  */
36319 Roo.tree.TreeFilter = function(tree, config){
36320     this.tree = tree;
36321     this.filtered = {};
36322     Roo.apply(this, config);
36323 };
36324
36325 Roo.tree.TreeFilter.prototype = {
36326     clearBlank:false,
36327     reverse:false,
36328     autoClear:false,
36329     remove:false,
36330
36331      /**
36332      * Filter the data by a specific attribute.
36333      * @param {String/RegExp} value Either string that the attribute value
36334      * should start with or a RegExp to test against the attribute
36335      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36336      * @param {TreeNode} startNode (optional) The node to start the filter at.
36337      */
36338     filter : function(value, attr, startNode){
36339         attr = attr || "text";
36340         var f;
36341         if(typeof value == "string"){
36342             var vlen = value.length;
36343             // auto clear empty filter
36344             if(vlen == 0 && this.clearBlank){
36345                 this.clear();
36346                 return;
36347             }
36348             value = value.toLowerCase();
36349             f = function(n){
36350                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36351             };
36352         }else if(value.exec){ // regex?
36353             f = function(n){
36354                 return value.test(n.attributes[attr]);
36355             };
36356         }else{
36357             throw 'Illegal filter type, must be string or regex';
36358         }
36359         this.filterBy(f, null, startNode);
36360         },
36361
36362     /**
36363      * Filter by a function. The passed function will be called with each
36364      * node in the tree (or from the startNode). If the function returns true, the node is kept
36365      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36366      * @param {Function} fn The filter function
36367      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36368      */
36369     filterBy : function(fn, scope, startNode){
36370         startNode = startNode || this.tree.root;
36371         if(this.autoClear){
36372             this.clear();
36373         }
36374         var af = this.filtered, rv = this.reverse;
36375         var f = function(n){
36376             if(n == startNode){
36377                 return true;
36378             }
36379             if(af[n.id]){
36380                 return false;
36381             }
36382             var m = fn.call(scope || n, n);
36383             if(!m || rv){
36384                 af[n.id] = n;
36385                 n.ui.hide();
36386                 return false;
36387             }
36388             return true;
36389         };
36390         startNode.cascade(f);
36391         if(this.remove){
36392            for(var id in af){
36393                if(typeof id != "function"){
36394                    var n = af[id];
36395                    if(n && n.parentNode){
36396                        n.parentNode.removeChild(n);
36397                    }
36398                }
36399            }
36400         }
36401     },
36402
36403     /**
36404      * Clears the current filter. Note: with the "remove" option
36405      * set a filter cannot be cleared.
36406      */
36407     clear : function(){
36408         var t = this.tree;
36409         var af = this.filtered;
36410         for(var id in af){
36411             if(typeof id != "function"){
36412                 var n = af[id];
36413                 if(n){
36414                     n.ui.show();
36415                 }
36416             }
36417         }
36418         this.filtered = {};
36419     }
36420 };
36421 /*
36422  * Based on:
36423  * Ext JS Library 1.1.1
36424  * Copyright(c) 2006-2007, Ext JS, LLC.
36425  *
36426  * Originally Released Under LGPL - original licence link has changed is not relivant.
36427  *
36428  * Fork - LGPL
36429  * <script type="text/javascript">
36430  */
36431  
36432
36433 /**
36434  * @class Roo.tree.TreeSorter
36435  * Provides sorting of nodes in a TreePanel
36436  * 
36437  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36438  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36439  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36440  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36441  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36442  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36443  * @constructor
36444  * @param {TreePanel} tree
36445  * @param {Object} config
36446  */
36447 Roo.tree.TreeSorter = function(tree, config){
36448     Roo.apply(this, config);
36449     tree.on("beforechildrenrendered", this.doSort, this);
36450     tree.on("append", this.updateSort, this);
36451     tree.on("insert", this.updateSort, this);
36452     
36453     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36454     var p = this.property || "text";
36455     var sortType = this.sortType;
36456     var fs = this.folderSort;
36457     var cs = this.caseSensitive === true;
36458     var leafAttr = this.leafAttr || 'leaf';
36459
36460     this.sortFn = function(n1, n2){
36461         if(fs){
36462             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36463                 return 1;
36464             }
36465             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36466                 return -1;
36467             }
36468         }
36469         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36470         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36471         if(v1 < v2){
36472                         return dsc ? +1 : -1;
36473                 }else if(v1 > v2){
36474                         return dsc ? -1 : +1;
36475         }else{
36476                 return 0;
36477         }
36478     };
36479 };
36480
36481 Roo.tree.TreeSorter.prototype = {
36482     doSort : function(node){
36483         node.sort(this.sortFn);
36484     },
36485     
36486     compareNodes : function(n1, n2){
36487         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36488     },
36489     
36490     updateSort : function(tree, node){
36491         if(node.childrenRendered){
36492             this.doSort.defer(1, this, [node]);
36493         }
36494     }
36495 };/*
36496  * Based on:
36497  * Ext JS Library 1.1.1
36498  * Copyright(c) 2006-2007, Ext JS, LLC.
36499  *
36500  * Originally Released Under LGPL - original licence link has changed is not relivant.
36501  *
36502  * Fork - LGPL
36503  * <script type="text/javascript">
36504  */
36505
36506 if(Roo.dd.DropZone){
36507     
36508 Roo.tree.TreeDropZone = function(tree, config){
36509     this.allowParentInsert = false;
36510     this.allowContainerDrop = false;
36511     this.appendOnly = false;
36512     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36513     this.tree = tree;
36514     this.lastInsertClass = "x-tree-no-status";
36515     this.dragOverData = {};
36516 };
36517
36518 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36519     ddGroup : "TreeDD",
36520     scroll:  true,
36521     
36522     expandDelay : 1000,
36523     
36524     expandNode : function(node){
36525         if(node.hasChildNodes() && !node.isExpanded()){
36526             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36527         }
36528     },
36529     
36530     queueExpand : function(node){
36531         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36532     },
36533     
36534     cancelExpand : function(){
36535         if(this.expandProcId){
36536             clearTimeout(this.expandProcId);
36537             this.expandProcId = false;
36538         }
36539     },
36540     
36541     isValidDropPoint : function(n, pt, dd, e, data){
36542         if(!n || !data){ return false; }
36543         var targetNode = n.node;
36544         var dropNode = data.node;
36545         // default drop rules
36546         if(!(targetNode && targetNode.isTarget && pt)){
36547             return false;
36548         }
36549         if(pt == "append" && targetNode.allowChildren === false){
36550             return false;
36551         }
36552         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36553             return false;
36554         }
36555         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36556             return false;
36557         }
36558         // reuse the object
36559         var overEvent = this.dragOverData;
36560         overEvent.tree = this.tree;
36561         overEvent.target = targetNode;
36562         overEvent.data = data;
36563         overEvent.point = pt;
36564         overEvent.source = dd;
36565         overEvent.rawEvent = e;
36566         overEvent.dropNode = dropNode;
36567         overEvent.cancel = false;  
36568         var result = this.tree.fireEvent("nodedragover", overEvent);
36569         return overEvent.cancel === false && result !== false;
36570     },
36571     
36572     getDropPoint : function(e, n, dd)
36573     {
36574         var tn = n.node;
36575         if(tn.isRoot){
36576             return tn.allowChildren !== false ? "append" : false; // always append for root
36577         }
36578         var dragEl = n.ddel;
36579         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36580         var y = Roo.lib.Event.getPageY(e);
36581         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36582         
36583         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36584         var noAppend = tn.allowChildren === false;
36585         if(this.appendOnly || tn.parentNode.allowChildren === false){
36586             return noAppend ? false : "append";
36587         }
36588         var noBelow = false;
36589         if(!this.allowParentInsert){
36590             noBelow = tn.hasChildNodes() && tn.isExpanded();
36591         }
36592         var q = (b - t) / (noAppend ? 2 : 3);
36593         if(y >= t && y < (t + q)){
36594             return "above";
36595         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36596             return "below";
36597         }else{
36598             return "append";
36599         }
36600     },
36601     
36602     onNodeEnter : function(n, dd, e, data)
36603     {
36604         this.cancelExpand();
36605     },
36606     
36607     onNodeOver : function(n, dd, e, data)
36608     {
36609        
36610         var pt = this.getDropPoint(e, n, dd);
36611         var node = n.node;
36612         
36613         // auto node expand check
36614         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36615             this.queueExpand(node);
36616         }else if(pt != "append"){
36617             this.cancelExpand();
36618         }
36619         
36620         // set the insert point style on the target node
36621         var returnCls = this.dropNotAllowed;
36622         if(this.isValidDropPoint(n, pt, dd, e, data)){
36623            if(pt){
36624                var el = n.ddel;
36625                var cls;
36626                if(pt == "above"){
36627                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36628                    cls = "x-tree-drag-insert-above";
36629                }else if(pt == "below"){
36630                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36631                    cls = "x-tree-drag-insert-below";
36632                }else{
36633                    returnCls = "x-tree-drop-ok-append";
36634                    cls = "x-tree-drag-append";
36635                }
36636                if(this.lastInsertClass != cls){
36637                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36638                    this.lastInsertClass = cls;
36639                }
36640            }
36641        }
36642        return returnCls;
36643     },
36644     
36645     onNodeOut : function(n, dd, e, data){
36646         
36647         this.cancelExpand();
36648         this.removeDropIndicators(n);
36649     },
36650     
36651     onNodeDrop : function(n, dd, e, data){
36652         var point = this.getDropPoint(e, n, dd);
36653         var targetNode = n.node;
36654         targetNode.ui.startDrop();
36655         if(!this.isValidDropPoint(n, point, dd, e, data)){
36656             targetNode.ui.endDrop();
36657             return false;
36658         }
36659         // first try to find the drop node
36660         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36661         var dropEvent = {
36662             tree : this.tree,
36663             target: targetNode,
36664             data: data,
36665             point: point,
36666             source: dd,
36667             rawEvent: e,
36668             dropNode: dropNode,
36669             cancel: !dropNode   
36670         };
36671         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36672         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36673             targetNode.ui.endDrop();
36674             return false;
36675         }
36676         // allow target changing
36677         targetNode = dropEvent.target;
36678         if(point == "append" && !targetNode.isExpanded()){
36679             targetNode.expand(false, null, function(){
36680                 this.completeDrop(dropEvent);
36681             }.createDelegate(this));
36682         }else{
36683             this.completeDrop(dropEvent);
36684         }
36685         return true;
36686     },
36687     
36688     completeDrop : function(de){
36689         var ns = de.dropNode, p = de.point, t = de.target;
36690         if(!(ns instanceof Array)){
36691             ns = [ns];
36692         }
36693         var n;
36694         for(var i = 0, len = ns.length; i < len; i++){
36695             n = ns[i];
36696             if(p == "above"){
36697                 t.parentNode.insertBefore(n, t);
36698             }else if(p == "below"){
36699                 t.parentNode.insertBefore(n, t.nextSibling);
36700             }else{
36701                 t.appendChild(n);
36702             }
36703         }
36704         n.ui.focus();
36705         if(this.tree.hlDrop){
36706             n.ui.highlight();
36707         }
36708         t.ui.endDrop();
36709         this.tree.fireEvent("nodedrop", de);
36710     },
36711     
36712     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36713         if(this.tree.hlDrop){
36714             dropNode.ui.focus();
36715             dropNode.ui.highlight();
36716         }
36717         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36718     },
36719     
36720     getTree : function(){
36721         return this.tree;
36722     },
36723     
36724     removeDropIndicators : function(n){
36725         if(n && n.ddel){
36726             var el = n.ddel;
36727             Roo.fly(el).removeClass([
36728                     "x-tree-drag-insert-above",
36729                     "x-tree-drag-insert-below",
36730                     "x-tree-drag-append"]);
36731             this.lastInsertClass = "_noclass";
36732         }
36733     },
36734     
36735     beforeDragDrop : function(target, e, id){
36736         this.cancelExpand();
36737         return true;
36738     },
36739     
36740     afterRepair : function(data){
36741         if(data && Roo.enableFx){
36742             data.node.ui.highlight();
36743         }
36744         this.hideProxy();
36745     } 
36746     
36747 });
36748
36749 }
36750 /*
36751  * Based on:
36752  * Ext JS Library 1.1.1
36753  * Copyright(c) 2006-2007, Ext JS, LLC.
36754  *
36755  * Originally Released Under LGPL - original licence link has changed is not relivant.
36756  *
36757  * Fork - LGPL
36758  * <script type="text/javascript">
36759  */
36760  
36761
36762 if(Roo.dd.DragZone){
36763 Roo.tree.TreeDragZone = function(tree, config){
36764     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36765     this.tree = tree;
36766 };
36767
36768 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36769     ddGroup : "TreeDD",
36770    
36771     onBeforeDrag : function(data, e){
36772         var n = data.node;
36773         return n && n.draggable && !n.disabled;
36774     },
36775      
36776     
36777     onInitDrag : function(e){
36778         var data = this.dragData;
36779         this.tree.getSelectionModel().select(data.node);
36780         this.proxy.update("");
36781         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36782         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36783     },
36784     
36785     getRepairXY : function(e, data){
36786         return data.node.ui.getDDRepairXY();
36787     },
36788     
36789     onEndDrag : function(data, e){
36790         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36791         
36792         
36793     },
36794     
36795     onValidDrop : function(dd, e, id){
36796         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36797         this.hideProxy();
36798     },
36799     
36800     beforeInvalidDrop : function(e, id){
36801         // this scrolls the original position back into view
36802         var sm = this.tree.getSelectionModel();
36803         sm.clearSelections();
36804         sm.select(this.dragData.node);
36805     }
36806 });
36807 }/*
36808  * Based on:
36809  * Ext JS Library 1.1.1
36810  * Copyright(c) 2006-2007, Ext JS, LLC.
36811  *
36812  * Originally Released Under LGPL - original licence link has changed is not relivant.
36813  *
36814  * Fork - LGPL
36815  * <script type="text/javascript">
36816  */
36817 /**
36818  * @class Roo.tree.TreeEditor
36819  * @extends Roo.Editor
36820  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36821  * as the editor field.
36822  * @constructor
36823  * @param {Object} config (used to be the tree panel.)
36824  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36825  * 
36826  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36827  * @cfg {Roo.form.TextField|Object} field The field configuration
36828  *
36829  * 
36830  */
36831 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36832     var tree = config;
36833     var field;
36834     if (oldconfig) { // old style..
36835         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36836     } else {
36837         // new style..
36838         tree = config.tree;
36839         config.field = config.field  || {};
36840         config.field.xtype = 'TextField';
36841         field = Roo.factory(config.field, Roo.form);
36842     }
36843     config = config || {};
36844     
36845     
36846     this.addEvents({
36847         /**
36848          * @event beforenodeedit
36849          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36850          * false from the handler of this event.
36851          * @param {Editor} this
36852          * @param {Roo.tree.Node} node 
36853          */
36854         "beforenodeedit" : true
36855     });
36856     
36857     //Roo.log(config);
36858     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36859
36860     this.tree = tree;
36861
36862     tree.on('beforeclick', this.beforeNodeClick, this);
36863     tree.getTreeEl().on('mousedown', this.hide, this);
36864     this.on('complete', this.updateNode, this);
36865     this.on('beforestartedit', this.fitToTree, this);
36866     this.on('startedit', this.bindScroll, this, {delay:10});
36867     this.on('specialkey', this.onSpecialKey, this);
36868 };
36869
36870 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36871     /**
36872      * @cfg {String} alignment
36873      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36874      */
36875     alignment: "l-l",
36876     // inherit
36877     autoSize: false,
36878     /**
36879      * @cfg {Boolean} hideEl
36880      * True to hide the bound element while the editor is displayed (defaults to false)
36881      */
36882     hideEl : false,
36883     /**
36884      * @cfg {String} cls
36885      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36886      */
36887     cls: "x-small-editor x-tree-editor",
36888     /**
36889      * @cfg {Boolean} shim
36890      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36891      */
36892     shim:false,
36893     // inherit
36894     shadow:"frame",
36895     /**
36896      * @cfg {Number} maxWidth
36897      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36898      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36899      * scroll and client offsets into account prior to each edit.
36900      */
36901     maxWidth: 250,
36902
36903     editDelay : 350,
36904
36905     // private
36906     fitToTree : function(ed, el){
36907         var td = this.tree.getTreeEl().dom, nd = el.dom;
36908         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36909             td.scrollLeft = nd.offsetLeft;
36910         }
36911         var w = Math.min(
36912                 this.maxWidth,
36913                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36914         this.setSize(w, '');
36915         
36916         return this.fireEvent('beforenodeedit', this, this.editNode);
36917         
36918     },
36919
36920     // private
36921     triggerEdit : function(node){
36922         this.completeEdit();
36923         this.editNode = node;
36924         this.startEdit(node.ui.textNode, node.text);
36925     },
36926
36927     // private
36928     bindScroll : function(){
36929         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36930     },
36931
36932     // private
36933     beforeNodeClick : function(node, e){
36934         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36935         this.lastClick = new Date();
36936         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36937             e.stopEvent();
36938             this.triggerEdit(node);
36939             return false;
36940         }
36941         return true;
36942     },
36943
36944     // private
36945     updateNode : function(ed, value){
36946         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36947         this.editNode.setText(value);
36948     },
36949
36950     // private
36951     onHide : function(){
36952         Roo.tree.TreeEditor.superclass.onHide.call(this);
36953         if(this.editNode){
36954             this.editNode.ui.focus();
36955         }
36956     },
36957
36958     // private
36959     onSpecialKey : function(field, e){
36960         var k = e.getKey();
36961         if(k == e.ESC){
36962             e.stopEvent();
36963             this.cancelEdit();
36964         }else if(k == e.ENTER && !e.hasModifier()){
36965             e.stopEvent();
36966             this.completeEdit();
36967         }
36968     }
36969 });//<Script type="text/javascript">
36970 /*
36971  * Based on:
36972  * Ext JS Library 1.1.1
36973  * Copyright(c) 2006-2007, Ext JS, LLC.
36974  *
36975  * Originally Released Under LGPL - original licence link has changed is not relivant.
36976  *
36977  * Fork - LGPL
36978  * <script type="text/javascript">
36979  */
36980  
36981 /**
36982  * Not documented??? - probably should be...
36983  */
36984
36985 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36986     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36987     
36988     renderElements : function(n, a, targetNode, bulkRender){
36989         //consel.log("renderElements?");
36990         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36991
36992         var t = n.getOwnerTree();
36993         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36994         
36995         var cols = t.columns;
36996         var bw = t.borderWidth;
36997         var c = cols[0];
36998         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36999          var cb = typeof a.checked == "boolean";
37000         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37001         var colcls = 'x-t-' + tid + '-c0';
37002         var buf = [
37003             '<li class="x-tree-node">',
37004             
37005                 
37006                 '<div class="x-tree-node-el ', a.cls,'">',
37007                     // extran...
37008                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37009                 
37010                 
37011                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37012                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37013                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37014                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37015                            (a.iconCls ? ' '+a.iconCls : ''),
37016                            '" unselectable="on" />',
37017                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37018                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37019                              
37020                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37021                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37022                             '<span unselectable="on" qtip="' + tx + '">',
37023                              tx,
37024                              '</span></a>' ,
37025                     '</div>',
37026                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37027                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37028                  ];
37029         for(var i = 1, len = cols.length; i < len; i++){
37030             c = cols[i];
37031             colcls = 'x-t-' + tid + '-c' +i;
37032             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37033             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37034                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37035                       "</div>");
37036          }
37037          
37038          buf.push(
37039             '</a>',
37040             '<div class="x-clear"></div></div>',
37041             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37042             "</li>");
37043         
37044         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37045             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37046                                 n.nextSibling.ui.getEl(), buf.join(""));
37047         }else{
37048             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37049         }
37050         var el = this.wrap.firstChild;
37051         this.elRow = el;
37052         this.elNode = el.firstChild;
37053         this.ranchor = el.childNodes[1];
37054         this.ctNode = this.wrap.childNodes[1];
37055         var cs = el.firstChild.childNodes;
37056         this.indentNode = cs[0];
37057         this.ecNode = cs[1];
37058         this.iconNode = cs[2];
37059         var index = 3;
37060         if(cb){
37061             this.checkbox = cs[3];
37062             index++;
37063         }
37064         this.anchor = cs[index];
37065         
37066         this.textNode = cs[index].firstChild;
37067         
37068         //el.on("click", this.onClick, this);
37069         //el.on("dblclick", this.onDblClick, this);
37070         
37071         
37072        // console.log(this);
37073     },
37074     initEvents : function(){
37075         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37076         
37077             
37078         var a = this.ranchor;
37079
37080         var el = Roo.get(a);
37081
37082         if(Roo.isOpera){ // opera render bug ignores the CSS
37083             el.setStyle("text-decoration", "none");
37084         }
37085
37086         el.on("click", this.onClick, this);
37087         el.on("dblclick", this.onDblClick, this);
37088         el.on("contextmenu", this.onContextMenu, this);
37089         
37090     },
37091     
37092     /*onSelectedChange : function(state){
37093         if(state){
37094             this.focus();
37095             this.addClass("x-tree-selected");
37096         }else{
37097             //this.blur();
37098             this.removeClass("x-tree-selected");
37099         }
37100     },*/
37101     addClass : function(cls){
37102         if(this.elRow){
37103             Roo.fly(this.elRow).addClass(cls);
37104         }
37105         
37106     },
37107     
37108     
37109     removeClass : function(cls){
37110         if(this.elRow){
37111             Roo.fly(this.elRow).removeClass(cls);
37112         }
37113     }
37114
37115     
37116     
37117 });//<Script type="text/javascript">
37118
37119 /*
37120  * Based on:
37121  * Ext JS Library 1.1.1
37122  * Copyright(c) 2006-2007, Ext JS, LLC.
37123  *
37124  * Originally Released Under LGPL - original licence link has changed is not relivant.
37125  *
37126  * Fork - LGPL
37127  * <script type="text/javascript">
37128  */
37129  
37130
37131 /**
37132  * @class Roo.tree.ColumnTree
37133  * @extends Roo.data.TreePanel
37134  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37135  * @cfg {int} borderWidth  compined right/left border allowance
37136  * @constructor
37137  * @param {String/HTMLElement/Element} el The container element
37138  * @param {Object} config
37139  */
37140 Roo.tree.ColumnTree =  function(el, config)
37141 {
37142    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37143    this.addEvents({
37144         /**
37145         * @event resize
37146         * Fire this event on a container when it resizes
37147         * @param {int} w Width
37148         * @param {int} h Height
37149         */
37150        "resize" : true
37151     });
37152     this.on('resize', this.onResize, this);
37153 };
37154
37155 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37156     //lines:false,
37157     
37158     
37159     borderWidth: Roo.isBorderBox ? 0 : 2, 
37160     headEls : false,
37161     
37162     render : function(){
37163         // add the header.....
37164        
37165         Roo.tree.ColumnTree.superclass.render.apply(this);
37166         
37167         this.el.addClass('x-column-tree');
37168         
37169         this.headers = this.el.createChild(
37170             {cls:'x-tree-headers'},this.innerCt.dom);
37171    
37172         var cols = this.columns, c;
37173         var totalWidth = 0;
37174         this.headEls = [];
37175         var  len = cols.length;
37176         for(var i = 0; i < len; i++){
37177              c = cols[i];
37178              totalWidth += c.width;
37179             this.headEls.push(this.headers.createChild({
37180                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37181                  cn: {
37182                      cls:'x-tree-hd-text',
37183                      html: c.header
37184                  },
37185                  style:'width:'+(c.width-this.borderWidth)+'px;'
37186              }));
37187         }
37188         this.headers.createChild({cls:'x-clear'});
37189         // prevent floats from wrapping when clipped
37190         this.headers.setWidth(totalWidth);
37191         //this.innerCt.setWidth(totalWidth);
37192         this.innerCt.setStyle({ overflow: 'auto' });
37193         this.onResize(this.width, this.height);
37194              
37195         
37196     },
37197     onResize : function(w,h)
37198     {
37199         this.height = h;
37200         this.width = w;
37201         // resize cols..
37202         this.innerCt.setWidth(this.width);
37203         this.innerCt.setHeight(this.height-20);
37204         
37205         // headers...
37206         var cols = this.columns, c;
37207         var totalWidth = 0;
37208         var expEl = false;
37209         var len = cols.length;
37210         for(var i = 0; i < len; i++){
37211             c = cols[i];
37212             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37213                 // it's the expander..
37214                 expEl  = this.headEls[i];
37215                 continue;
37216             }
37217             totalWidth += c.width;
37218             
37219         }
37220         if (expEl) {
37221             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37222         }
37223         this.headers.setWidth(w-20);
37224
37225         
37226         
37227         
37228     }
37229 });
37230 /*
37231  * Based on:
37232  * Ext JS Library 1.1.1
37233  * Copyright(c) 2006-2007, Ext JS, LLC.
37234  *
37235  * Originally Released Under LGPL - original licence link has changed is not relivant.
37236  *
37237  * Fork - LGPL
37238  * <script type="text/javascript">
37239  */
37240  
37241 /**
37242  * @class Roo.menu.Menu
37243  * @extends Roo.util.Observable
37244  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37245  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37246  * @constructor
37247  * Creates a new Menu
37248  * @param {Object} config Configuration options
37249  */
37250 Roo.menu.Menu = function(config){
37251     
37252     Roo.menu.Menu.superclass.constructor.call(this, config);
37253     
37254     this.id = this.id || Roo.id();
37255     this.addEvents({
37256         /**
37257          * @event beforeshow
37258          * Fires before this menu is displayed
37259          * @param {Roo.menu.Menu} this
37260          */
37261         beforeshow : true,
37262         /**
37263          * @event beforehide
37264          * Fires before this menu is hidden
37265          * @param {Roo.menu.Menu} this
37266          */
37267         beforehide : true,
37268         /**
37269          * @event show
37270          * Fires after this menu is displayed
37271          * @param {Roo.menu.Menu} this
37272          */
37273         show : true,
37274         /**
37275          * @event hide
37276          * Fires after this menu is hidden
37277          * @param {Roo.menu.Menu} this
37278          */
37279         hide : true,
37280         /**
37281          * @event click
37282          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37283          * @param {Roo.menu.Menu} this
37284          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37285          * @param {Roo.EventObject} e
37286          */
37287         click : true,
37288         /**
37289          * @event mouseover
37290          * Fires when the mouse is hovering over this menu
37291          * @param {Roo.menu.Menu} this
37292          * @param {Roo.EventObject} e
37293          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37294          */
37295         mouseover : true,
37296         /**
37297          * @event mouseout
37298          * Fires when the mouse exits this menu
37299          * @param {Roo.menu.Menu} this
37300          * @param {Roo.EventObject} e
37301          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37302          */
37303         mouseout : true,
37304         /**
37305          * @event itemclick
37306          * Fires when a menu item contained in this menu is clicked
37307          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37308          * @param {Roo.EventObject} e
37309          */
37310         itemclick: true
37311     });
37312     if (this.registerMenu) {
37313         Roo.menu.MenuMgr.register(this);
37314     }
37315     
37316     var mis = this.items;
37317     this.items = new Roo.util.MixedCollection();
37318     if(mis){
37319         this.add.apply(this, mis);
37320     }
37321 };
37322
37323 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37324     /**
37325      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37326      */
37327     minWidth : 120,
37328     /**
37329      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37330      * for bottom-right shadow (defaults to "sides")
37331      */
37332     shadow : "sides",
37333     /**
37334      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37335      * this menu (defaults to "tl-tr?")
37336      */
37337     subMenuAlign : "tl-tr?",
37338     /**
37339      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37340      * relative to its element of origin (defaults to "tl-bl?")
37341      */
37342     defaultAlign : "tl-bl?",
37343     /**
37344      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37345      */
37346     allowOtherMenus : false,
37347     /**
37348      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37349      */
37350     registerMenu : true,
37351
37352     hidden:true,
37353
37354     // private
37355     render : function(){
37356         if(this.el){
37357             return;
37358         }
37359         var el = this.el = new Roo.Layer({
37360             cls: "x-menu",
37361             shadow:this.shadow,
37362             constrain: false,
37363             parentEl: this.parentEl || document.body,
37364             zindex:15000
37365         });
37366
37367         this.keyNav = new Roo.menu.MenuNav(this);
37368
37369         if(this.plain){
37370             el.addClass("x-menu-plain");
37371         }
37372         if(this.cls){
37373             el.addClass(this.cls);
37374         }
37375         // generic focus element
37376         this.focusEl = el.createChild({
37377             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37378         });
37379         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37380         //disabling touch- as it's causing issues ..
37381         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37382         ul.on('click'   , this.onClick, this);
37383         
37384         
37385         ul.on("mouseover", this.onMouseOver, this);
37386         ul.on("mouseout", this.onMouseOut, this);
37387         this.items.each(function(item){
37388             if (item.hidden) {
37389                 return;
37390             }
37391             
37392             var li = document.createElement("li");
37393             li.className = "x-menu-list-item";
37394             ul.dom.appendChild(li);
37395             item.render(li, this);
37396         }, this);
37397         this.ul = ul;
37398         this.autoWidth();
37399     },
37400
37401     // private
37402     autoWidth : function(){
37403         var el = this.el, ul = this.ul;
37404         if(!el){
37405             return;
37406         }
37407         var w = this.width;
37408         if(w){
37409             el.setWidth(w);
37410         }else if(Roo.isIE){
37411             el.setWidth(this.minWidth);
37412             var t = el.dom.offsetWidth; // force recalc
37413             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37414         }
37415     },
37416
37417     // private
37418     delayAutoWidth : function(){
37419         if(this.rendered){
37420             if(!this.awTask){
37421                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37422             }
37423             this.awTask.delay(20);
37424         }
37425     },
37426
37427     // private
37428     findTargetItem : function(e){
37429         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37430         if(t && t.menuItemId){
37431             return this.items.get(t.menuItemId);
37432         }
37433     },
37434
37435     // private
37436     onClick : function(e){
37437         Roo.log("menu.onClick");
37438         var t = this.findTargetItem(e);
37439         if(!t){
37440             return;
37441         }
37442         Roo.log(e);
37443         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37444             if(t == this.activeItem && t.shouldDeactivate(e)){
37445                 this.activeItem.deactivate();
37446                 delete this.activeItem;
37447                 return;
37448             }
37449             if(t.canActivate){
37450                 this.setActiveItem(t, true);
37451             }
37452             return;
37453             
37454             
37455         }
37456         
37457         t.onClick(e);
37458         this.fireEvent("click", this, t, e);
37459     },
37460
37461     // private
37462     setActiveItem : function(item, autoExpand){
37463         if(item != this.activeItem){
37464             if(this.activeItem){
37465                 this.activeItem.deactivate();
37466             }
37467             this.activeItem = item;
37468             item.activate(autoExpand);
37469         }else if(autoExpand){
37470             item.expandMenu();
37471         }
37472     },
37473
37474     // private
37475     tryActivate : function(start, step){
37476         var items = this.items;
37477         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37478             var item = items.get(i);
37479             if(!item.disabled && item.canActivate){
37480                 this.setActiveItem(item, false);
37481                 return item;
37482             }
37483         }
37484         return false;
37485     },
37486
37487     // private
37488     onMouseOver : function(e){
37489         var t;
37490         if(t = this.findTargetItem(e)){
37491             if(t.canActivate && !t.disabled){
37492                 this.setActiveItem(t, true);
37493             }
37494         }
37495         this.fireEvent("mouseover", this, e, t);
37496     },
37497
37498     // private
37499     onMouseOut : function(e){
37500         var t;
37501         if(t = this.findTargetItem(e)){
37502             if(t == this.activeItem && t.shouldDeactivate(e)){
37503                 this.activeItem.deactivate();
37504                 delete this.activeItem;
37505             }
37506         }
37507         this.fireEvent("mouseout", this, e, t);
37508     },
37509
37510     /**
37511      * Read-only.  Returns true if the menu is currently displayed, else false.
37512      * @type Boolean
37513      */
37514     isVisible : function(){
37515         return this.el && !this.hidden;
37516     },
37517
37518     /**
37519      * Displays this menu relative to another element
37520      * @param {String/HTMLElement/Roo.Element} element The element to align to
37521      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37522      * the element (defaults to this.defaultAlign)
37523      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37524      */
37525     show : function(el, pos, parentMenu){
37526         this.parentMenu = parentMenu;
37527         if(!this.el){
37528             this.render();
37529         }
37530         this.fireEvent("beforeshow", this);
37531         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37532     },
37533
37534     /**
37535      * Displays this menu at a specific xy position
37536      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37537      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37538      */
37539     showAt : function(xy, parentMenu, /* private: */_e){
37540         this.parentMenu = parentMenu;
37541         if(!this.el){
37542             this.render();
37543         }
37544         if(_e !== false){
37545             this.fireEvent("beforeshow", this);
37546             xy = this.el.adjustForConstraints(xy);
37547         }
37548         this.el.setXY(xy);
37549         this.el.show();
37550         this.hidden = false;
37551         this.focus();
37552         this.fireEvent("show", this);
37553     },
37554
37555     focus : function(){
37556         if(!this.hidden){
37557             this.doFocus.defer(50, this);
37558         }
37559     },
37560
37561     doFocus : function(){
37562         if(!this.hidden){
37563             this.focusEl.focus();
37564         }
37565     },
37566
37567     /**
37568      * Hides this menu and optionally all parent menus
37569      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37570      */
37571     hide : function(deep){
37572         if(this.el && this.isVisible()){
37573             this.fireEvent("beforehide", this);
37574             if(this.activeItem){
37575                 this.activeItem.deactivate();
37576                 this.activeItem = null;
37577             }
37578             this.el.hide();
37579             this.hidden = true;
37580             this.fireEvent("hide", this);
37581         }
37582         if(deep === true && this.parentMenu){
37583             this.parentMenu.hide(true);
37584         }
37585     },
37586
37587     /**
37588      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37589      * Any of the following are valid:
37590      * <ul>
37591      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37592      * <li>An HTMLElement object which will be converted to a menu item</li>
37593      * <li>A menu item config object that will be created as a new menu item</li>
37594      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37595      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37596      * </ul>
37597      * Usage:
37598      * <pre><code>
37599 // Create the menu
37600 var menu = new Roo.menu.Menu();
37601
37602 // Create a menu item to add by reference
37603 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37604
37605 // Add a bunch of items at once using different methods.
37606 // Only the last item added will be returned.
37607 var item = menu.add(
37608     menuItem,                // add existing item by ref
37609     'Dynamic Item',          // new TextItem
37610     '-',                     // new separator
37611     { text: 'Config Item' }  // new item by config
37612 );
37613 </code></pre>
37614      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37615      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37616      */
37617     add : function(){
37618         var a = arguments, l = a.length, item;
37619         for(var i = 0; i < l; i++){
37620             var el = a[i];
37621             if ((typeof(el) == "object") && el.xtype && el.xns) {
37622                 el = Roo.factory(el, Roo.menu);
37623             }
37624             
37625             if(el.render){ // some kind of Item
37626                 item = this.addItem(el);
37627             }else if(typeof el == "string"){ // string
37628                 if(el == "separator" || el == "-"){
37629                     item = this.addSeparator();
37630                 }else{
37631                     item = this.addText(el);
37632                 }
37633             }else if(el.tagName || el.el){ // element
37634                 item = this.addElement(el);
37635             }else if(typeof el == "object"){ // must be menu item config?
37636                 item = this.addMenuItem(el);
37637             }
37638         }
37639         return item;
37640     },
37641
37642     /**
37643      * Returns this menu's underlying {@link Roo.Element} object
37644      * @return {Roo.Element} The element
37645      */
37646     getEl : function(){
37647         if(!this.el){
37648             this.render();
37649         }
37650         return this.el;
37651     },
37652
37653     /**
37654      * Adds a separator bar to the menu
37655      * @return {Roo.menu.Item} The menu item that was added
37656      */
37657     addSeparator : function(){
37658         return this.addItem(new Roo.menu.Separator());
37659     },
37660
37661     /**
37662      * Adds an {@link Roo.Element} object to the menu
37663      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37664      * @return {Roo.menu.Item} The menu item that was added
37665      */
37666     addElement : function(el){
37667         return this.addItem(new Roo.menu.BaseItem(el));
37668     },
37669
37670     /**
37671      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37672      * @param {Roo.menu.Item} item The menu item to add
37673      * @return {Roo.menu.Item} The menu item that was added
37674      */
37675     addItem : function(item){
37676         this.items.add(item);
37677         if(this.ul){
37678             var li = document.createElement("li");
37679             li.className = "x-menu-list-item";
37680             this.ul.dom.appendChild(li);
37681             item.render(li, this);
37682             this.delayAutoWidth();
37683         }
37684         return item;
37685     },
37686
37687     /**
37688      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37689      * @param {Object} config A MenuItem config object
37690      * @return {Roo.menu.Item} The menu item that was added
37691      */
37692     addMenuItem : function(config){
37693         if(!(config instanceof Roo.menu.Item)){
37694             if(typeof config.checked == "boolean"){ // must be check menu item config?
37695                 config = new Roo.menu.CheckItem(config);
37696             }else{
37697                 config = new Roo.menu.Item(config);
37698             }
37699         }
37700         return this.addItem(config);
37701     },
37702
37703     /**
37704      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37705      * @param {String} text The text to display in the menu item
37706      * @return {Roo.menu.Item} The menu item that was added
37707      */
37708     addText : function(text){
37709         return this.addItem(new Roo.menu.TextItem({ text : text }));
37710     },
37711
37712     /**
37713      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37714      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37715      * @param {Roo.menu.Item} item The menu item to add
37716      * @return {Roo.menu.Item} The menu item that was added
37717      */
37718     insert : function(index, item){
37719         this.items.insert(index, item);
37720         if(this.ul){
37721             var li = document.createElement("li");
37722             li.className = "x-menu-list-item";
37723             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37724             item.render(li, this);
37725             this.delayAutoWidth();
37726         }
37727         return item;
37728     },
37729
37730     /**
37731      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37732      * @param {Roo.menu.Item} item The menu item to remove
37733      */
37734     remove : function(item){
37735         this.items.removeKey(item.id);
37736         item.destroy();
37737     },
37738
37739     /**
37740      * Removes and destroys all items in the menu
37741      */
37742     removeAll : function(){
37743         var f;
37744         while(f = this.items.first()){
37745             this.remove(f);
37746         }
37747     }
37748 });
37749
37750 // MenuNav is a private utility class used internally by the Menu
37751 Roo.menu.MenuNav = function(menu){
37752     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37753     this.scope = this.menu = menu;
37754 };
37755
37756 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37757     doRelay : function(e, h){
37758         var k = e.getKey();
37759         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37760             this.menu.tryActivate(0, 1);
37761             return false;
37762         }
37763         return h.call(this.scope || this, e, this.menu);
37764     },
37765
37766     up : function(e, m){
37767         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37768             m.tryActivate(m.items.length-1, -1);
37769         }
37770     },
37771
37772     down : function(e, m){
37773         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37774             m.tryActivate(0, 1);
37775         }
37776     },
37777
37778     right : function(e, m){
37779         if(m.activeItem){
37780             m.activeItem.expandMenu(true);
37781         }
37782     },
37783
37784     left : function(e, m){
37785         m.hide();
37786         if(m.parentMenu && m.parentMenu.activeItem){
37787             m.parentMenu.activeItem.activate();
37788         }
37789     },
37790
37791     enter : function(e, m){
37792         if(m.activeItem){
37793             e.stopPropagation();
37794             m.activeItem.onClick(e);
37795             m.fireEvent("click", this, m.activeItem);
37796             return true;
37797         }
37798     }
37799 });/*
37800  * Based on:
37801  * Ext JS Library 1.1.1
37802  * Copyright(c) 2006-2007, Ext JS, LLC.
37803  *
37804  * Originally Released Under LGPL - original licence link has changed is not relivant.
37805  *
37806  * Fork - LGPL
37807  * <script type="text/javascript">
37808  */
37809  
37810 /**
37811  * @class Roo.menu.MenuMgr
37812  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37813  * @singleton
37814  */
37815 Roo.menu.MenuMgr = function(){
37816    var menus, active, groups = {}, attached = false, lastShow = new Date();
37817
37818    // private - called when first menu is created
37819    function init(){
37820        menus = {};
37821        active = new Roo.util.MixedCollection();
37822        Roo.get(document).addKeyListener(27, function(){
37823            if(active.length > 0){
37824                hideAll();
37825            }
37826        });
37827    }
37828
37829    // private
37830    function hideAll(){
37831        if(active && active.length > 0){
37832            var c = active.clone();
37833            c.each(function(m){
37834                m.hide();
37835            });
37836        }
37837    }
37838
37839    // private
37840    function onHide(m){
37841        active.remove(m);
37842        if(active.length < 1){
37843            Roo.get(document).un("mousedown", onMouseDown);
37844            attached = false;
37845        }
37846    }
37847
37848    // private
37849    function onShow(m){
37850        var last = active.last();
37851        lastShow = new Date();
37852        active.add(m);
37853        if(!attached){
37854            Roo.get(document).on("mousedown", onMouseDown);
37855            attached = true;
37856        }
37857        if(m.parentMenu){
37858           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37859           m.parentMenu.activeChild = m;
37860        }else if(last && last.isVisible()){
37861           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37862        }
37863    }
37864
37865    // private
37866    function onBeforeHide(m){
37867        if(m.activeChild){
37868            m.activeChild.hide();
37869        }
37870        if(m.autoHideTimer){
37871            clearTimeout(m.autoHideTimer);
37872            delete m.autoHideTimer;
37873        }
37874    }
37875
37876    // private
37877    function onBeforeShow(m){
37878        var pm = m.parentMenu;
37879        if(!pm && !m.allowOtherMenus){
37880            hideAll();
37881        }else if(pm && pm.activeChild && active != m){
37882            pm.activeChild.hide();
37883        }
37884    }
37885
37886    // private
37887    function onMouseDown(e){
37888        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37889            hideAll();
37890        }
37891    }
37892
37893    // private
37894    function onBeforeCheck(mi, state){
37895        if(state){
37896            var g = groups[mi.group];
37897            for(var i = 0, l = g.length; i < l; i++){
37898                if(g[i] != mi){
37899                    g[i].setChecked(false);
37900                }
37901            }
37902        }
37903    }
37904
37905    return {
37906
37907        /**
37908         * Hides all menus that are currently visible
37909         */
37910        hideAll : function(){
37911             hideAll();  
37912        },
37913
37914        // private
37915        register : function(menu){
37916            if(!menus){
37917                init();
37918            }
37919            menus[menu.id] = menu;
37920            menu.on("beforehide", onBeforeHide);
37921            menu.on("hide", onHide);
37922            menu.on("beforeshow", onBeforeShow);
37923            menu.on("show", onShow);
37924            var g = menu.group;
37925            if(g && menu.events["checkchange"]){
37926                if(!groups[g]){
37927                    groups[g] = [];
37928                }
37929                groups[g].push(menu);
37930                menu.on("checkchange", onCheck);
37931            }
37932        },
37933
37934         /**
37935          * Returns a {@link Roo.menu.Menu} object
37936          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37937          * be used to generate and return a new Menu instance.
37938          */
37939        get : function(menu){
37940            if(typeof menu == "string"){ // menu id
37941                return menus[menu];
37942            }else if(menu.events){  // menu instance
37943                return menu;
37944            }else if(typeof menu.length == 'number'){ // array of menu items?
37945                return new Roo.menu.Menu({items:menu});
37946            }else{ // otherwise, must be a config
37947                return new Roo.menu.Menu(menu);
37948            }
37949        },
37950
37951        // private
37952        unregister : function(menu){
37953            delete menus[menu.id];
37954            menu.un("beforehide", onBeforeHide);
37955            menu.un("hide", onHide);
37956            menu.un("beforeshow", onBeforeShow);
37957            menu.un("show", onShow);
37958            var g = menu.group;
37959            if(g && menu.events["checkchange"]){
37960                groups[g].remove(menu);
37961                menu.un("checkchange", onCheck);
37962            }
37963        },
37964
37965        // private
37966        registerCheckable : function(menuItem){
37967            var g = menuItem.group;
37968            if(g){
37969                if(!groups[g]){
37970                    groups[g] = [];
37971                }
37972                groups[g].push(menuItem);
37973                menuItem.on("beforecheckchange", onBeforeCheck);
37974            }
37975        },
37976
37977        // private
37978        unregisterCheckable : function(menuItem){
37979            var g = menuItem.group;
37980            if(g){
37981                groups[g].remove(menuItem);
37982                menuItem.un("beforecheckchange", onBeforeCheck);
37983            }
37984        }
37985    };
37986 }();/*
37987  * Based on:
37988  * Ext JS Library 1.1.1
37989  * Copyright(c) 2006-2007, Ext JS, LLC.
37990  *
37991  * Originally Released Under LGPL - original licence link has changed is not relivant.
37992  *
37993  * Fork - LGPL
37994  * <script type="text/javascript">
37995  */
37996  
37997
37998 /**
37999  * @class Roo.menu.BaseItem
38000  * @extends Roo.Component
38001  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38002  * management and base configuration options shared by all menu components.
38003  * @constructor
38004  * Creates a new BaseItem
38005  * @param {Object} config Configuration options
38006  */
38007 Roo.menu.BaseItem = function(config){
38008     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38009
38010     this.addEvents({
38011         /**
38012          * @event click
38013          * Fires when this item is clicked
38014          * @param {Roo.menu.BaseItem} this
38015          * @param {Roo.EventObject} e
38016          */
38017         click: true,
38018         /**
38019          * @event activate
38020          * Fires when this item is activated
38021          * @param {Roo.menu.BaseItem} this
38022          */
38023         activate : true,
38024         /**
38025          * @event deactivate
38026          * Fires when this item is deactivated
38027          * @param {Roo.menu.BaseItem} this
38028          */
38029         deactivate : true
38030     });
38031
38032     if(this.handler){
38033         this.on("click", this.handler, this.scope, true);
38034     }
38035 };
38036
38037 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38038     /**
38039      * @cfg {Function} handler
38040      * A function that will handle the click event of this menu item (defaults to undefined)
38041      */
38042     /**
38043      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38044      */
38045     canActivate : false,
38046     
38047      /**
38048      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38049      */
38050     hidden: false,
38051     
38052     /**
38053      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38054      */
38055     activeClass : "x-menu-item-active",
38056     /**
38057      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38058      */
38059     hideOnClick : true,
38060     /**
38061      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38062      */
38063     hideDelay : 100,
38064
38065     // private
38066     ctype: "Roo.menu.BaseItem",
38067
38068     // private
38069     actionMode : "container",
38070
38071     // private
38072     render : function(container, parentMenu){
38073         this.parentMenu = parentMenu;
38074         Roo.menu.BaseItem.superclass.render.call(this, container);
38075         this.container.menuItemId = this.id;
38076     },
38077
38078     // private
38079     onRender : function(container, position){
38080         this.el = Roo.get(this.el);
38081         container.dom.appendChild(this.el.dom);
38082     },
38083
38084     // private
38085     onClick : function(e){
38086         if(!this.disabled && this.fireEvent("click", this, e) !== false
38087                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38088             this.handleClick(e);
38089         }else{
38090             e.stopEvent();
38091         }
38092     },
38093
38094     // private
38095     activate : function(){
38096         if(this.disabled){
38097             return false;
38098         }
38099         var li = this.container;
38100         li.addClass(this.activeClass);
38101         this.region = li.getRegion().adjust(2, 2, -2, -2);
38102         this.fireEvent("activate", this);
38103         return true;
38104     },
38105
38106     // private
38107     deactivate : function(){
38108         this.container.removeClass(this.activeClass);
38109         this.fireEvent("deactivate", this);
38110     },
38111
38112     // private
38113     shouldDeactivate : function(e){
38114         return !this.region || !this.region.contains(e.getPoint());
38115     },
38116
38117     // private
38118     handleClick : function(e){
38119         if(this.hideOnClick){
38120             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38121         }
38122     },
38123
38124     // private
38125     expandMenu : function(autoActivate){
38126         // do nothing
38127     },
38128
38129     // private
38130     hideMenu : function(){
38131         // do nothing
38132     }
38133 });/*
38134  * Based on:
38135  * Ext JS Library 1.1.1
38136  * Copyright(c) 2006-2007, Ext JS, LLC.
38137  *
38138  * Originally Released Under LGPL - original licence link has changed is not relivant.
38139  *
38140  * Fork - LGPL
38141  * <script type="text/javascript">
38142  */
38143  
38144 /**
38145  * @class Roo.menu.Adapter
38146  * @extends Roo.menu.BaseItem
38147  * 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.
38148  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38149  * @constructor
38150  * Creates a new Adapter
38151  * @param {Object} config Configuration options
38152  */
38153 Roo.menu.Adapter = function(component, config){
38154     Roo.menu.Adapter.superclass.constructor.call(this, config);
38155     this.component = component;
38156 };
38157 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38158     // private
38159     canActivate : true,
38160
38161     // private
38162     onRender : function(container, position){
38163         this.component.render(container);
38164         this.el = this.component.getEl();
38165     },
38166
38167     // private
38168     activate : function(){
38169         if(this.disabled){
38170             return false;
38171         }
38172         this.component.focus();
38173         this.fireEvent("activate", this);
38174         return true;
38175     },
38176
38177     // private
38178     deactivate : function(){
38179         this.fireEvent("deactivate", this);
38180     },
38181
38182     // private
38183     disable : function(){
38184         this.component.disable();
38185         Roo.menu.Adapter.superclass.disable.call(this);
38186     },
38187
38188     // private
38189     enable : function(){
38190         this.component.enable();
38191         Roo.menu.Adapter.superclass.enable.call(this);
38192     }
38193 });/*
38194  * Based on:
38195  * Ext JS Library 1.1.1
38196  * Copyright(c) 2006-2007, Ext JS, LLC.
38197  *
38198  * Originally Released Under LGPL - original licence link has changed is not relivant.
38199  *
38200  * Fork - LGPL
38201  * <script type="text/javascript">
38202  */
38203
38204 /**
38205  * @class Roo.menu.TextItem
38206  * @extends Roo.menu.BaseItem
38207  * Adds a static text string to a menu, usually used as either a heading or group separator.
38208  * Note: old style constructor with text is still supported.
38209  * 
38210  * @constructor
38211  * Creates a new TextItem
38212  * @param {Object} cfg Configuration
38213  */
38214 Roo.menu.TextItem = function(cfg){
38215     if (typeof(cfg) == 'string') {
38216         this.text = cfg;
38217     } else {
38218         Roo.apply(this,cfg);
38219     }
38220     
38221     Roo.menu.TextItem.superclass.constructor.call(this);
38222 };
38223
38224 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38225     /**
38226      * @cfg {Boolean} text Text to show on item.
38227      */
38228     text : '',
38229     
38230     /**
38231      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38232      */
38233     hideOnClick : false,
38234     /**
38235      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38236      */
38237     itemCls : "x-menu-text",
38238
38239     // private
38240     onRender : function(){
38241         var s = document.createElement("span");
38242         s.className = this.itemCls;
38243         s.innerHTML = this.text;
38244         this.el = s;
38245         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38246     }
38247 });/*
38248  * Based on:
38249  * Ext JS Library 1.1.1
38250  * Copyright(c) 2006-2007, Ext JS, LLC.
38251  *
38252  * Originally Released Under LGPL - original licence link has changed is not relivant.
38253  *
38254  * Fork - LGPL
38255  * <script type="text/javascript">
38256  */
38257
38258 /**
38259  * @class Roo.menu.Separator
38260  * @extends Roo.menu.BaseItem
38261  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38262  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38263  * @constructor
38264  * @param {Object} config Configuration options
38265  */
38266 Roo.menu.Separator = function(config){
38267     Roo.menu.Separator.superclass.constructor.call(this, config);
38268 };
38269
38270 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38271     /**
38272      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38273      */
38274     itemCls : "x-menu-sep",
38275     /**
38276      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38277      */
38278     hideOnClick : false,
38279
38280     // private
38281     onRender : function(li){
38282         var s = document.createElement("span");
38283         s.className = this.itemCls;
38284         s.innerHTML = "&#160;";
38285         this.el = s;
38286         li.addClass("x-menu-sep-li");
38287         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38288     }
38289 });/*
38290  * Based on:
38291  * Ext JS Library 1.1.1
38292  * Copyright(c) 2006-2007, Ext JS, LLC.
38293  *
38294  * Originally Released Under LGPL - original licence link has changed is not relivant.
38295  *
38296  * Fork - LGPL
38297  * <script type="text/javascript">
38298  */
38299 /**
38300  * @class Roo.menu.Item
38301  * @extends Roo.menu.BaseItem
38302  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38303  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38304  * activation and click handling.
38305  * @constructor
38306  * Creates a new Item
38307  * @param {Object} config Configuration options
38308  */
38309 Roo.menu.Item = function(config){
38310     Roo.menu.Item.superclass.constructor.call(this, config);
38311     if(this.menu){
38312         this.menu = Roo.menu.MenuMgr.get(this.menu);
38313     }
38314 };
38315 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38316     
38317     /**
38318      * @cfg {String} text
38319      * The text to show on the menu item.
38320      */
38321     text: '',
38322      /**
38323      * @cfg {String} HTML to render in menu
38324      * The text to show on the menu item (HTML version).
38325      */
38326     html: '',
38327     /**
38328      * @cfg {String} icon
38329      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38330      */
38331     icon: undefined,
38332     /**
38333      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38334      */
38335     itemCls : "x-menu-item",
38336     /**
38337      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38338      */
38339     canActivate : true,
38340     /**
38341      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38342      */
38343     showDelay: 200,
38344     // doc'd in BaseItem
38345     hideDelay: 200,
38346
38347     // private
38348     ctype: "Roo.menu.Item",
38349     
38350     // private
38351     onRender : function(container, position){
38352         var el = document.createElement("a");
38353         el.hideFocus = true;
38354         el.unselectable = "on";
38355         el.href = this.href || "#";
38356         if(this.hrefTarget){
38357             el.target = this.hrefTarget;
38358         }
38359         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38360         
38361         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38362         
38363         el.innerHTML = String.format(
38364                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38365                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38366         this.el = el;
38367         Roo.menu.Item.superclass.onRender.call(this, container, position);
38368     },
38369
38370     /**
38371      * Sets the text to display in this menu item
38372      * @param {String} text The text to display
38373      * @param {Boolean} isHTML true to indicate text is pure html.
38374      */
38375     setText : function(text, isHTML){
38376         if (isHTML) {
38377             this.html = text;
38378         } else {
38379             this.text = text;
38380             this.html = '';
38381         }
38382         if(this.rendered){
38383             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38384      
38385             this.el.update(String.format(
38386                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38387                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38388             this.parentMenu.autoWidth();
38389         }
38390     },
38391
38392     // private
38393     handleClick : function(e){
38394         if(!this.href){ // if no link defined, stop the event automatically
38395             e.stopEvent();
38396         }
38397         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38398     },
38399
38400     // private
38401     activate : function(autoExpand){
38402         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38403             this.focus();
38404             if(autoExpand){
38405                 this.expandMenu();
38406             }
38407         }
38408         return true;
38409     },
38410
38411     // private
38412     shouldDeactivate : function(e){
38413         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38414             if(this.menu && this.menu.isVisible()){
38415                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38416             }
38417             return true;
38418         }
38419         return false;
38420     },
38421
38422     // private
38423     deactivate : function(){
38424         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38425         this.hideMenu();
38426     },
38427
38428     // private
38429     expandMenu : function(autoActivate){
38430         if(!this.disabled && this.menu){
38431             clearTimeout(this.hideTimer);
38432             delete this.hideTimer;
38433             if(!this.menu.isVisible() && !this.showTimer){
38434                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38435             }else if (this.menu.isVisible() && autoActivate){
38436                 this.menu.tryActivate(0, 1);
38437             }
38438         }
38439     },
38440
38441     // private
38442     deferExpand : function(autoActivate){
38443         delete this.showTimer;
38444         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38445         if(autoActivate){
38446             this.menu.tryActivate(0, 1);
38447         }
38448     },
38449
38450     // private
38451     hideMenu : function(){
38452         clearTimeout(this.showTimer);
38453         delete this.showTimer;
38454         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38455             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38456         }
38457     },
38458
38459     // private
38460     deferHide : function(){
38461         delete this.hideTimer;
38462         this.menu.hide();
38463     }
38464 });/*
38465  * Based on:
38466  * Ext JS Library 1.1.1
38467  * Copyright(c) 2006-2007, Ext JS, LLC.
38468  *
38469  * Originally Released Under LGPL - original licence link has changed is not relivant.
38470  *
38471  * Fork - LGPL
38472  * <script type="text/javascript">
38473  */
38474  
38475 /**
38476  * @class Roo.menu.CheckItem
38477  * @extends Roo.menu.Item
38478  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38479  * @constructor
38480  * Creates a new CheckItem
38481  * @param {Object} config Configuration options
38482  */
38483 Roo.menu.CheckItem = function(config){
38484     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38485     this.addEvents({
38486         /**
38487          * @event beforecheckchange
38488          * Fires before the checked value is set, providing an opportunity to cancel if needed
38489          * @param {Roo.menu.CheckItem} this
38490          * @param {Boolean} checked The new checked value that will be set
38491          */
38492         "beforecheckchange" : true,
38493         /**
38494          * @event checkchange
38495          * Fires after the checked value has been set
38496          * @param {Roo.menu.CheckItem} this
38497          * @param {Boolean} checked The checked value that was set
38498          */
38499         "checkchange" : true
38500     });
38501     if(this.checkHandler){
38502         this.on('checkchange', this.checkHandler, this.scope);
38503     }
38504 };
38505 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38506     /**
38507      * @cfg {String} group
38508      * All check items with the same group name will automatically be grouped into a single-select
38509      * radio button group (defaults to '')
38510      */
38511     /**
38512      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38513      */
38514     itemCls : "x-menu-item x-menu-check-item",
38515     /**
38516      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38517      */
38518     groupClass : "x-menu-group-item",
38519
38520     /**
38521      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38522      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38523      * initialized with checked = true will be rendered as checked.
38524      */
38525     checked: false,
38526
38527     // private
38528     ctype: "Roo.menu.CheckItem",
38529
38530     // private
38531     onRender : function(c){
38532         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38533         if(this.group){
38534             this.el.addClass(this.groupClass);
38535         }
38536         Roo.menu.MenuMgr.registerCheckable(this);
38537         if(this.checked){
38538             this.checked = false;
38539             this.setChecked(true, true);
38540         }
38541     },
38542
38543     // private
38544     destroy : function(){
38545         if(this.rendered){
38546             Roo.menu.MenuMgr.unregisterCheckable(this);
38547         }
38548         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38549     },
38550
38551     /**
38552      * Set the checked state of this item
38553      * @param {Boolean} checked The new checked value
38554      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38555      */
38556     setChecked : function(state, suppressEvent){
38557         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38558             if(this.container){
38559                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38560             }
38561             this.checked = state;
38562             if(suppressEvent !== true){
38563                 this.fireEvent("checkchange", this, state);
38564             }
38565         }
38566     },
38567
38568     // private
38569     handleClick : function(e){
38570        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38571            this.setChecked(!this.checked);
38572        }
38573        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38574     }
38575 });/*
38576  * Based on:
38577  * Ext JS Library 1.1.1
38578  * Copyright(c) 2006-2007, Ext JS, LLC.
38579  *
38580  * Originally Released Under LGPL - original licence link has changed is not relivant.
38581  *
38582  * Fork - LGPL
38583  * <script type="text/javascript">
38584  */
38585  
38586 /**
38587  * @class Roo.menu.DateItem
38588  * @extends Roo.menu.Adapter
38589  * A menu item that wraps the {@link Roo.DatPicker} component.
38590  * @constructor
38591  * Creates a new DateItem
38592  * @param {Object} config Configuration options
38593  */
38594 Roo.menu.DateItem = function(config){
38595     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38596     /** The Roo.DatePicker object @type Roo.DatePicker */
38597     this.picker = this.component;
38598     this.addEvents({select: true});
38599     
38600     this.picker.on("render", function(picker){
38601         picker.getEl().swallowEvent("click");
38602         picker.container.addClass("x-menu-date-item");
38603     });
38604
38605     this.picker.on("select", this.onSelect, this);
38606 };
38607
38608 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38609     // private
38610     onSelect : function(picker, date){
38611         this.fireEvent("select", this, date, picker);
38612         Roo.menu.DateItem.superclass.handleClick.call(this);
38613     }
38614 });/*
38615  * Based on:
38616  * Ext JS Library 1.1.1
38617  * Copyright(c) 2006-2007, Ext JS, LLC.
38618  *
38619  * Originally Released Under LGPL - original licence link has changed is not relivant.
38620  *
38621  * Fork - LGPL
38622  * <script type="text/javascript">
38623  */
38624  
38625 /**
38626  * @class Roo.menu.ColorItem
38627  * @extends Roo.menu.Adapter
38628  * A menu item that wraps the {@link Roo.ColorPalette} component.
38629  * @constructor
38630  * Creates a new ColorItem
38631  * @param {Object} config Configuration options
38632  */
38633 Roo.menu.ColorItem = function(config){
38634     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38635     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38636     this.palette = this.component;
38637     this.relayEvents(this.palette, ["select"]);
38638     if(this.selectHandler){
38639         this.on('select', this.selectHandler, this.scope);
38640     }
38641 };
38642 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38643  * Based on:
38644  * Ext JS Library 1.1.1
38645  * Copyright(c) 2006-2007, Ext JS, LLC.
38646  *
38647  * Originally Released Under LGPL - original licence link has changed is not relivant.
38648  *
38649  * Fork - LGPL
38650  * <script type="text/javascript">
38651  */
38652  
38653
38654 /**
38655  * @class Roo.menu.DateMenu
38656  * @extends Roo.menu.Menu
38657  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38658  * @constructor
38659  * Creates a new DateMenu
38660  * @param {Object} config Configuration options
38661  */
38662 Roo.menu.DateMenu = function(config){
38663     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38664     this.plain = true;
38665     var di = new Roo.menu.DateItem(config);
38666     this.add(di);
38667     /**
38668      * The {@link Roo.DatePicker} instance for this DateMenu
38669      * @type DatePicker
38670      */
38671     this.picker = di.picker;
38672     /**
38673      * @event select
38674      * @param {DatePicker} picker
38675      * @param {Date} date
38676      */
38677     this.relayEvents(di, ["select"]);
38678     this.on('beforeshow', function(){
38679         if(this.picker){
38680             this.picker.hideMonthPicker(false);
38681         }
38682     }, this);
38683 };
38684 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38685     cls:'x-date-menu'
38686 });/*
38687  * Based on:
38688  * Ext JS Library 1.1.1
38689  * Copyright(c) 2006-2007, Ext JS, LLC.
38690  *
38691  * Originally Released Under LGPL - original licence link has changed is not relivant.
38692  *
38693  * Fork - LGPL
38694  * <script type="text/javascript">
38695  */
38696  
38697
38698 /**
38699  * @class Roo.menu.ColorMenu
38700  * @extends Roo.menu.Menu
38701  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38702  * @constructor
38703  * Creates a new ColorMenu
38704  * @param {Object} config Configuration options
38705  */
38706 Roo.menu.ColorMenu = function(config){
38707     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38708     this.plain = true;
38709     var ci = new Roo.menu.ColorItem(config);
38710     this.add(ci);
38711     /**
38712      * The {@link Roo.ColorPalette} instance for this ColorMenu
38713      * @type ColorPalette
38714      */
38715     this.palette = ci.palette;
38716     /**
38717      * @event select
38718      * @param {ColorPalette} palette
38719      * @param {String} color
38720      */
38721     this.relayEvents(ci, ["select"]);
38722 };
38723 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38724  * Based on:
38725  * Ext JS Library 1.1.1
38726  * Copyright(c) 2006-2007, Ext JS, LLC.
38727  *
38728  * Originally Released Under LGPL - original licence link has changed is not relivant.
38729  *
38730  * Fork - LGPL
38731  * <script type="text/javascript">
38732  */
38733  
38734 /**
38735  * @class Roo.form.TextItem
38736  * @extends Roo.BoxComponent
38737  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38738  * @constructor
38739  * Creates a new TextItem
38740  * @param {Object} config Configuration options
38741  */
38742 Roo.form.TextItem = function(config){
38743     Roo.form.TextItem.superclass.constructor.call(this, config);
38744 };
38745
38746 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38747     
38748     /**
38749      * @cfg {String} tag the tag for this item (default div)
38750      */
38751     tag : 'div',
38752     /**
38753      * @cfg {String} html the content for this item
38754      */
38755     html : '',
38756     
38757     getAutoCreate : function()
38758     {
38759         var cfg = {
38760             id: this.id,
38761             tag: this.tag,
38762             html: this.html,
38763             cls: 'x-form-item'
38764         };
38765         
38766         return cfg;
38767         
38768     },
38769     
38770     onRender : function(ct, position)
38771     {
38772         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38773         
38774         if(!this.el){
38775             var cfg = this.getAutoCreate();
38776             if(!cfg.name){
38777                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38778             }
38779             if (!cfg.name.length) {
38780                 delete cfg.name;
38781             }
38782             this.el = ct.createChild(cfg, position);
38783         }
38784     }
38785     
38786 });/*
38787  * Based on:
38788  * Ext JS Library 1.1.1
38789  * Copyright(c) 2006-2007, Ext JS, LLC.
38790  *
38791  * Originally Released Under LGPL - original licence link has changed is not relivant.
38792  *
38793  * Fork - LGPL
38794  * <script type="text/javascript">
38795  */
38796  
38797 /**
38798  * @class Roo.form.Field
38799  * @extends Roo.BoxComponent
38800  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38801  * @constructor
38802  * Creates a new Field
38803  * @param {Object} config Configuration options
38804  */
38805 Roo.form.Field = function(config){
38806     Roo.form.Field.superclass.constructor.call(this, config);
38807 };
38808
38809 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38810     /**
38811      * @cfg {String} fieldLabel Label to use when rendering a form.
38812      */
38813        /**
38814      * @cfg {String} qtip Mouse over tip
38815      */
38816      
38817     /**
38818      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38819      */
38820     invalidClass : "x-form-invalid",
38821     /**
38822      * @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")
38823      */
38824     invalidText : "The value in this field is invalid",
38825     /**
38826      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38827      */
38828     focusClass : "x-form-focus",
38829     /**
38830      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38831       automatic validation (defaults to "keyup").
38832      */
38833     validationEvent : "keyup",
38834     /**
38835      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38836      */
38837     validateOnBlur : true,
38838     /**
38839      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38840      */
38841     validationDelay : 250,
38842     /**
38843      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38844      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38845      */
38846     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38847     /**
38848      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38849      */
38850     fieldClass : "x-form-field",
38851     /**
38852      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38853      *<pre>
38854 Value         Description
38855 -----------   ----------------------------------------------------------------------
38856 qtip          Display a quick tip when the user hovers over the field
38857 title         Display a default browser title attribute popup
38858 under         Add a block div beneath the field containing the error text
38859 side          Add an error icon to the right of the field with a popup on hover
38860 [element id]  Add the error text directly to the innerHTML of the specified element
38861 </pre>
38862      */
38863     msgTarget : 'qtip',
38864     /**
38865      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38866      */
38867     msgFx : 'normal',
38868
38869     /**
38870      * @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.
38871      */
38872     readOnly : false,
38873
38874     /**
38875      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38876      */
38877     disabled : false,
38878
38879     /**
38880      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38881      */
38882     inputType : undefined,
38883     
38884     /**
38885      * @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).
38886          */
38887         tabIndex : undefined,
38888         
38889     // private
38890     isFormField : true,
38891
38892     // private
38893     hasFocus : false,
38894     /**
38895      * @property {Roo.Element} fieldEl
38896      * Element Containing the rendered Field (with label etc.)
38897      */
38898     /**
38899      * @cfg {Mixed} value A value to initialize this field with.
38900      */
38901     value : undefined,
38902
38903     /**
38904      * @cfg {String} name The field's HTML name attribute.
38905      */
38906     /**
38907      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38908      */
38909     // private
38910     loadedValue : false,
38911      
38912      
38913         // private ??
38914         initComponent : function(){
38915         Roo.form.Field.superclass.initComponent.call(this);
38916         this.addEvents({
38917             /**
38918              * @event focus
38919              * Fires when this field receives input focus.
38920              * @param {Roo.form.Field} this
38921              */
38922             focus : true,
38923             /**
38924              * @event blur
38925              * Fires when this field loses input focus.
38926              * @param {Roo.form.Field} this
38927              */
38928             blur : true,
38929             /**
38930              * @event specialkey
38931              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38932              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38933              * @param {Roo.form.Field} this
38934              * @param {Roo.EventObject} e The event object
38935              */
38936             specialkey : true,
38937             /**
38938              * @event change
38939              * Fires just before the field blurs if the field value has changed.
38940              * @param {Roo.form.Field} this
38941              * @param {Mixed} newValue The new value
38942              * @param {Mixed} oldValue The original value
38943              */
38944             change : true,
38945             /**
38946              * @event invalid
38947              * Fires after the field has been marked as invalid.
38948              * @param {Roo.form.Field} this
38949              * @param {String} msg The validation message
38950              */
38951             invalid : true,
38952             /**
38953              * @event valid
38954              * Fires after the field has been validated with no errors.
38955              * @param {Roo.form.Field} this
38956              */
38957             valid : true,
38958              /**
38959              * @event keyup
38960              * Fires after the key up
38961              * @param {Roo.form.Field} this
38962              * @param {Roo.EventObject}  e The event Object
38963              */
38964             keyup : true
38965         });
38966     },
38967
38968     /**
38969      * Returns the name attribute of the field if available
38970      * @return {String} name The field name
38971      */
38972     getName: function(){
38973          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38974     },
38975
38976     // private
38977     onRender : function(ct, position){
38978         Roo.form.Field.superclass.onRender.call(this, ct, position);
38979         if(!this.el){
38980             var cfg = this.getAutoCreate();
38981             if(!cfg.name){
38982                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38983             }
38984             if (!cfg.name.length) {
38985                 delete cfg.name;
38986             }
38987             if(this.inputType){
38988                 cfg.type = this.inputType;
38989             }
38990             this.el = ct.createChild(cfg, position);
38991         }
38992         var type = this.el.dom.type;
38993         if(type){
38994             if(type == 'password'){
38995                 type = 'text';
38996             }
38997             this.el.addClass('x-form-'+type);
38998         }
38999         if(this.readOnly){
39000             this.el.dom.readOnly = true;
39001         }
39002         if(this.tabIndex !== undefined){
39003             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39004         }
39005
39006         this.el.addClass([this.fieldClass, this.cls]);
39007         this.initValue();
39008     },
39009
39010     /**
39011      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39012      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39013      * @return {Roo.form.Field} this
39014      */
39015     applyTo : function(target){
39016         this.allowDomMove = false;
39017         this.el = Roo.get(target);
39018         this.render(this.el.dom.parentNode);
39019         return this;
39020     },
39021
39022     // private
39023     initValue : function(){
39024         if(this.value !== undefined){
39025             this.setValue(this.value);
39026         }else if(this.el.dom.value.length > 0){
39027             this.setValue(this.el.dom.value);
39028         }
39029     },
39030
39031     /**
39032      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39033      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39034      */
39035     isDirty : function() {
39036         if(this.disabled) {
39037             return false;
39038         }
39039         return String(this.getValue()) !== String(this.originalValue);
39040     },
39041
39042     /**
39043      * stores the current value in loadedValue
39044      */
39045     resetHasChanged : function()
39046     {
39047         this.loadedValue = String(this.getValue());
39048     },
39049     /**
39050      * checks the current value against the 'loaded' value.
39051      * Note - will return false if 'resetHasChanged' has not been called first.
39052      */
39053     hasChanged : function()
39054     {
39055         if(this.disabled || this.readOnly) {
39056             return false;
39057         }
39058         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39059     },
39060     
39061     
39062     
39063     // private
39064     afterRender : function(){
39065         Roo.form.Field.superclass.afterRender.call(this);
39066         this.initEvents();
39067     },
39068
39069     // private
39070     fireKey : function(e){
39071         //Roo.log('field ' + e.getKey());
39072         if(e.isNavKeyPress()){
39073             this.fireEvent("specialkey", this, e);
39074         }
39075     },
39076
39077     /**
39078      * Resets the current field value to the originally loaded value and clears any validation messages
39079      */
39080     reset : function(){
39081         this.setValue(this.resetValue);
39082         this.originalValue = this.getValue();
39083         this.clearInvalid();
39084     },
39085
39086     // private
39087     initEvents : function(){
39088         // safari killled keypress - so keydown is now used..
39089         this.el.on("keydown" , this.fireKey,  this);
39090         this.el.on("focus", this.onFocus,  this);
39091         this.el.on("blur", this.onBlur,  this);
39092         this.el.relayEvent('keyup', this);
39093
39094         // reference to original value for reset
39095         this.originalValue = this.getValue();
39096         this.resetValue =  this.getValue();
39097     },
39098
39099     // private
39100     onFocus : function(){
39101         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39102             this.el.addClass(this.focusClass);
39103         }
39104         if(!this.hasFocus){
39105             this.hasFocus = true;
39106             this.startValue = this.getValue();
39107             this.fireEvent("focus", this);
39108         }
39109     },
39110
39111     beforeBlur : Roo.emptyFn,
39112
39113     // private
39114     onBlur : function(){
39115         this.beforeBlur();
39116         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39117             this.el.removeClass(this.focusClass);
39118         }
39119         this.hasFocus = false;
39120         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39121             this.validate();
39122         }
39123         var v = this.getValue();
39124         if(String(v) !== String(this.startValue)){
39125             this.fireEvent('change', this, v, this.startValue);
39126         }
39127         this.fireEvent("blur", this);
39128     },
39129
39130     /**
39131      * Returns whether or not the field value is currently valid
39132      * @param {Boolean} preventMark True to disable marking the field invalid
39133      * @return {Boolean} True if the value is valid, else false
39134      */
39135     isValid : function(preventMark){
39136         if(this.disabled){
39137             return true;
39138         }
39139         var restore = this.preventMark;
39140         this.preventMark = preventMark === true;
39141         var v = this.validateValue(this.processValue(this.getRawValue()));
39142         this.preventMark = restore;
39143         return v;
39144     },
39145
39146     /**
39147      * Validates the field value
39148      * @return {Boolean} True if the value is valid, else false
39149      */
39150     validate : function(){
39151         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39152             this.clearInvalid();
39153             return true;
39154         }
39155         return false;
39156     },
39157
39158     processValue : function(value){
39159         return value;
39160     },
39161
39162     // private
39163     // Subclasses should provide the validation implementation by overriding this
39164     validateValue : function(value){
39165         return true;
39166     },
39167
39168     /**
39169      * Mark this field as invalid
39170      * @param {String} msg The validation message
39171      */
39172     markInvalid : function(msg){
39173         if(!this.rendered || this.preventMark){ // not rendered
39174             return;
39175         }
39176         
39177         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39178         
39179         obj.el.addClass(this.invalidClass);
39180         msg = msg || this.invalidText;
39181         switch(this.msgTarget){
39182             case 'qtip':
39183                 obj.el.dom.qtip = msg;
39184                 obj.el.dom.qclass = 'x-form-invalid-tip';
39185                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39186                     Roo.QuickTips.enable();
39187                 }
39188                 break;
39189             case 'title':
39190                 this.el.dom.title = msg;
39191                 break;
39192             case 'under':
39193                 if(!this.errorEl){
39194                     var elp = this.el.findParent('.x-form-element', 5, true);
39195                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39196                     this.errorEl.setWidth(elp.getWidth(true)-20);
39197                 }
39198                 this.errorEl.update(msg);
39199                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39200                 break;
39201             case 'side':
39202                 if(!this.errorIcon){
39203                     var elp = this.el.findParent('.x-form-element', 5, true);
39204                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39205                 }
39206                 this.alignErrorIcon();
39207                 this.errorIcon.dom.qtip = msg;
39208                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39209                 this.errorIcon.show();
39210                 this.on('resize', this.alignErrorIcon, this);
39211                 break;
39212             default:
39213                 var t = Roo.getDom(this.msgTarget);
39214                 t.innerHTML = msg;
39215                 t.style.display = this.msgDisplay;
39216                 break;
39217         }
39218         this.fireEvent('invalid', this, msg);
39219     },
39220
39221     // private
39222     alignErrorIcon : function(){
39223         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39224     },
39225
39226     /**
39227      * Clear any invalid styles/messages for this field
39228      */
39229     clearInvalid : function(){
39230         if(!this.rendered || this.preventMark){ // not rendered
39231             return;
39232         }
39233         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39234         
39235         obj.el.removeClass(this.invalidClass);
39236         switch(this.msgTarget){
39237             case 'qtip':
39238                 obj.el.dom.qtip = '';
39239                 break;
39240             case 'title':
39241                 this.el.dom.title = '';
39242                 break;
39243             case 'under':
39244                 if(this.errorEl){
39245                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39246                 }
39247                 break;
39248             case 'side':
39249                 if(this.errorIcon){
39250                     this.errorIcon.dom.qtip = '';
39251                     this.errorIcon.hide();
39252                     this.un('resize', this.alignErrorIcon, this);
39253                 }
39254                 break;
39255             default:
39256                 var t = Roo.getDom(this.msgTarget);
39257                 t.innerHTML = '';
39258                 t.style.display = 'none';
39259                 break;
39260         }
39261         this.fireEvent('valid', this);
39262     },
39263
39264     /**
39265      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39266      * @return {Mixed} value The field value
39267      */
39268     getRawValue : function(){
39269         var v = this.el.getValue();
39270         
39271         return v;
39272     },
39273
39274     /**
39275      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39276      * @return {Mixed} value The field value
39277      */
39278     getValue : function(){
39279         var v = this.el.getValue();
39280          
39281         return v;
39282     },
39283
39284     /**
39285      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39286      * @param {Mixed} value The value to set
39287      */
39288     setRawValue : function(v){
39289         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39290     },
39291
39292     /**
39293      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39294      * @param {Mixed} value The value to set
39295      */
39296     setValue : function(v){
39297         this.value = v;
39298         if(this.rendered){
39299             this.el.dom.value = (v === null || v === undefined ? '' : v);
39300              this.validate();
39301         }
39302     },
39303
39304     adjustSize : function(w, h){
39305         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39306         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39307         return s;
39308     },
39309
39310     adjustWidth : function(tag, w){
39311         tag = tag.toLowerCase();
39312         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39313             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39314                 if(tag == 'input'){
39315                     return w + 2;
39316                 }
39317                 if(tag == 'textarea'){
39318                     return w-2;
39319                 }
39320             }else if(Roo.isOpera){
39321                 if(tag == 'input'){
39322                     return w + 2;
39323                 }
39324                 if(tag == 'textarea'){
39325                     return w-2;
39326                 }
39327             }
39328         }
39329         return w;
39330     }
39331 });
39332
39333
39334 // anything other than normal should be considered experimental
39335 Roo.form.Field.msgFx = {
39336     normal : {
39337         show: function(msgEl, f){
39338             msgEl.setDisplayed('block');
39339         },
39340
39341         hide : function(msgEl, f){
39342             msgEl.setDisplayed(false).update('');
39343         }
39344     },
39345
39346     slide : {
39347         show: function(msgEl, f){
39348             msgEl.slideIn('t', {stopFx:true});
39349         },
39350
39351         hide : function(msgEl, f){
39352             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39353         }
39354     },
39355
39356     slideRight : {
39357         show: function(msgEl, f){
39358             msgEl.fixDisplay();
39359             msgEl.alignTo(f.el, 'tl-tr');
39360             msgEl.slideIn('l', {stopFx:true});
39361         },
39362
39363         hide : function(msgEl, f){
39364             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39365         }
39366     }
39367 };/*
39368  * Based on:
39369  * Ext JS Library 1.1.1
39370  * Copyright(c) 2006-2007, Ext JS, LLC.
39371  *
39372  * Originally Released Under LGPL - original licence link has changed is not relivant.
39373  *
39374  * Fork - LGPL
39375  * <script type="text/javascript">
39376  */
39377  
39378
39379 /**
39380  * @class Roo.form.TextField
39381  * @extends Roo.form.Field
39382  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39383  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39384  * @constructor
39385  * Creates a new TextField
39386  * @param {Object} config Configuration options
39387  */
39388 Roo.form.TextField = function(config){
39389     Roo.form.TextField.superclass.constructor.call(this, config);
39390     this.addEvents({
39391         /**
39392          * @event autosize
39393          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39394          * according to the default logic, but this event provides a hook for the developer to apply additional
39395          * logic at runtime to resize the field if needed.
39396              * @param {Roo.form.Field} this This text field
39397              * @param {Number} width The new field width
39398              */
39399         autosize : true
39400     });
39401 };
39402
39403 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39404     /**
39405      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39406      */
39407     grow : false,
39408     /**
39409      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39410      */
39411     growMin : 30,
39412     /**
39413      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39414      */
39415     growMax : 800,
39416     /**
39417      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39418      */
39419     vtype : null,
39420     /**
39421      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39422      */
39423     maskRe : null,
39424     /**
39425      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39426      */
39427     disableKeyFilter : false,
39428     /**
39429      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39430      */
39431     allowBlank : true,
39432     /**
39433      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39434      */
39435     minLength : 0,
39436     /**
39437      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39438      */
39439     maxLength : Number.MAX_VALUE,
39440     /**
39441      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39442      */
39443     minLengthText : "The minimum length for this field is {0}",
39444     /**
39445      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39446      */
39447     maxLengthText : "The maximum length for this field is {0}",
39448     /**
39449      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39450      */
39451     selectOnFocus : false,
39452     /**
39453      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39454      */    
39455     allowLeadingSpace : false,
39456     /**
39457      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39458      */
39459     blankText : "This field is required",
39460     /**
39461      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39462      * If available, this function will be called only after the basic validators all return true, and will be passed the
39463      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39464      */
39465     validator : null,
39466     /**
39467      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39468      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39469      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39470      */
39471     regex : null,
39472     /**
39473      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39474      */
39475     regexText : "",
39476     /**
39477      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39478      */
39479     emptyText : null,
39480    
39481
39482     // private
39483     initEvents : function()
39484     {
39485         if (this.emptyText) {
39486             this.el.attr('placeholder', this.emptyText);
39487         }
39488         
39489         Roo.form.TextField.superclass.initEvents.call(this);
39490         if(this.validationEvent == 'keyup'){
39491             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39492             this.el.on('keyup', this.filterValidation, this);
39493         }
39494         else if(this.validationEvent !== false){
39495             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39496         }
39497         
39498         if(this.selectOnFocus){
39499             this.on("focus", this.preFocus, this);
39500         }
39501         if (!this.allowLeadingSpace) {
39502             this.on('blur', this.cleanLeadingSpace, this);
39503         }
39504         
39505         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39506             this.el.on("keypress", this.filterKeys, this);
39507         }
39508         if(this.grow){
39509             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39510             this.el.on("click", this.autoSize,  this);
39511         }
39512         if(this.el.is('input[type=password]') && Roo.isSafari){
39513             this.el.on('keydown', this.SafariOnKeyDown, this);
39514         }
39515     },
39516
39517     processValue : function(value){
39518         if(this.stripCharsRe){
39519             var newValue = value.replace(this.stripCharsRe, '');
39520             if(newValue !== value){
39521                 this.setRawValue(newValue);
39522                 return newValue;
39523             }
39524         }
39525         return value;
39526     },
39527
39528     filterValidation : function(e){
39529         if(!e.isNavKeyPress()){
39530             this.validationTask.delay(this.validationDelay);
39531         }
39532     },
39533
39534     // private
39535     onKeyUp : function(e){
39536         if(!e.isNavKeyPress()){
39537             this.autoSize();
39538         }
39539     },
39540     // private - clean the leading white space
39541     cleanLeadingSpace : function(e)
39542     {
39543         if ( this.inputType == 'file') {
39544             return;
39545         }
39546         
39547         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39548     },
39549     /**
39550      * Resets the current field value to the originally-loaded value and clears any validation messages.
39551      *  
39552      */
39553     reset : function(){
39554         Roo.form.TextField.superclass.reset.call(this);
39555        
39556     }, 
39557     // private
39558     preFocus : function(){
39559         
39560         if(this.selectOnFocus){
39561             this.el.dom.select();
39562         }
39563     },
39564
39565     
39566     // private
39567     filterKeys : function(e){
39568         var k = e.getKey();
39569         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39570             return;
39571         }
39572         var c = e.getCharCode(), cc = String.fromCharCode(c);
39573         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39574             return;
39575         }
39576         if(!this.maskRe.test(cc)){
39577             e.stopEvent();
39578         }
39579     },
39580
39581     setValue : function(v){
39582         
39583         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39584         
39585         this.autoSize();
39586     },
39587
39588     /**
39589      * Validates a value according to the field's validation rules and marks the field as invalid
39590      * if the validation fails
39591      * @param {Mixed} value The value to validate
39592      * @return {Boolean} True if the value is valid, else false
39593      */
39594     validateValue : function(value){
39595         if(value.length < 1)  { // if it's blank
39596              if(this.allowBlank){
39597                 this.clearInvalid();
39598                 return true;
39599              }else{
39600                 this.markInvalid(this.blankText);
39601                 return false;
39602              }
39603         }
39604         if(value.length < this.minLength){
39605             this.markInvalid(String.format(this.minLengthText, this.minLength));
39606             return false;
39607         }
39608         if(value.length > this.maxLength){
39609             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39610             return false;
39611         }
39612         if(this.vtype){
39613             var vt = Roo.form.VTypes;
39614             if(!vt[this.vtype](value, this)){
39615                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39616                 return false;
39617             }
39618         }
39619         if(typeof this.validator == "function"){
39620             var msg = this.validator(value);
39621             if(msg !== true){
39622                 this.markInvalid(msg);
39623                 return false;
39624             }
39625         }
39626         if(this.regex && !this.regex.test(value)){
39627             this.markInvalid(this.regexText);
39628             return false;
39629         }
39630         return true;
39631     },
39632
39633     /**
39634      * Selects text in this field
39635      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39636      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39637      */
39638     selectText : function(start, end){
39639         var v = this.getRawValue();
39640         if(v.length > 0){
39641             start = start === undefined ? 0 : start;
39642             end = end === undefined ? v.length : end;
39643             var d = this.el.dom;
39644             if(d.setSelectionRange){
39645                 d.setSelectionRange(start, end);
39646             }else if(d.createTextRange){
39647                 var range = d.createTextRange();
39648                 range.moveStart("character", start);
39649                 range.moveEnd("character", v.length-end);
39650                 range.select();
39651             }
39652         }
39653     },
39654
39655     /**
39656      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39657      * This only takes effect if grow = true, and fires the autosize event.
39658      */
39659     autoSize : function(){
39660         if(!this.grow || !this.rendered){
39661             return;
39662         }
39663         if(!this.metrics){
39664             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39665         }
39666         var el = this.el;
39667         var v = el.dom.value;
39668         var d = document.createElement('div');
39669         d.appendChild(document.createTextNode(v));
39670         v = d.innerHTML;
39671         d = null;
39672         v += "&#160;";
39673         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39674         this.el.setWidth(w);
39675         this.fireEvent("autosize", this, w);
39676     },
39677     
39678     // private
39679     SafariOnKeyDown : function(event)
39680     {
39681         // this is a workaround for a password hang bug on chrome/ webkit.
39682         
39683         var isSelectAll = false;
39684         
39685         if(this.el.dom.selectionEnd > 0){
39686             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39687         }
39688         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39689             event.preventDefault();
39690             this.setValue('');
39691             return;
39692         }
39693         
39694         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39695             
39696             event.preventDefault();
39697             // this is very hacky as keydown always get's upper case.
39698             
39699             var cc = String.fromCharCode(event.getCharCode());
39700             
39701             
39702             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39703             
39704         }
39705         
39706         
39707     }
39708 });/*
39709  * Based on:
39710  * Ext JS Library 1.1.1
39711  * Copyright(c) 2006-2007, Ext JS, LLC.
39712  *
39713  * Originally Released Under LGPL - original licence link has changed is not relivant.
39714  *
39715  * Fork - LGPL
39716  * <script type="text/javascript">
39717  */
39718  
39719 /**
39720  * @class Roo.form.Hidden
39721  * @extends Roo.form.TextField
39722  * Simple Hidden element used on forms 
39723  * 
39724  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39725  * 
39726  * @constructor
39727  * Creates a new Hidden form element.
39728  * @param {Object} config Configuration options
39729  */
39730
39731
39732
39733 // easy hidden field...
39734 Roo.form.Hidden = function(config){
39735     Roo.form.Hidden.superclass.constructor.call(this, config);
39736 };
39737   
39738 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39739     fieldLabel:      '',
39740     inputType:      'hidden',
39741     width:          50,
39742     allowBlank:     true,
39743     labelSeparator: '',
39744     hidden:         true,
39745     itemCls :       'x-form-item-display-none'
39746
39747
39748 });
39749
39750
39751 /*
39752  * Based on:
39753  * Ext JS Library 1.1.1
39754  * Copyright(c) 2006-2007, Ext JS, LLC.
39755  *
39756  * Originally Released Under LGPL - original licence link has changed is not relivant.
39757  *
39758  * Fork - LGPL
39759  * <script type="text/javascript">
39760  */
39761  
39762 /**
39763  * @class Roo.form.TriggerField
39764  * @extends Roo.form.TextField
39765  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39766  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39767  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39768  * for which you can provide a custom implementation.  For example:
39769  * <pre><code>
39770 var trigger = new Roo.form.TriggerField();
39771 trigger.onTriggerClick = myTriggerFn;
39772 trigger.applyTo('my-field');
39773 </code></pre>
39774  *
39775  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39776  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39777  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39778  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39779  * @constructor
39780  * Create a new TriggerField.
39781  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39782  * to the base TextField)
39783  */
39784 Roo.form.TriggerField = function(config){
39785     this.mimicing = false;
39786     Roo.form.TriggerField.superclass.constructor.call(this, config);
39787 };
39788
39789 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39790     /**
39791      * @cfg {String} triggerClass A CSS class to apply to the trigger
39792      */
39793     /**
39794      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39795      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39796      */
39797     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39798     /**
39799      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39800      */
39801     hideTrigger:false,
39802
39803     /** @cfg {Boolean} grow @hide */
39804     /** @cfg {Number} growMin @hide */
39805     /** @cfg {Number} growMax @hide */
39806
39807     /**
39808      * @hide 
39809      * @method
39810      */
39811     autoSize: Roo.emptyFn,
39812     // private
39813     monitorTab : true,
39814     // private
39815     deferHeight : true,
39816
39817     
39818     actionMode : 'wrap',
39819     // private
39820     onResize : function(w, h){
39821         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39822         if(typeof w == 'number'){
39823             var x = w - this.trigger.getWidth();
39824             this.el.setWidth(this.adjustWidth('input', x));
39825             this.trigger.setStyle('left', x+'px');
39826         }
39827     },
39828
39829     // private
39830     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39831
39832     // private
39833     getResizeEl : function(){
39834         return this.wrap;
39835     },
39836
39837     // private
39838     getPositionEl : function(){
39839         return this.wrap;
39840     },
39841
39842     // private
39843     alignErrorIcon : function(){
39844         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39845     },
39846
39847     // private
39848     onRender : function(ct, position){
39849         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39850         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39851         this.trigger = this.wrap.createChild(this.triggerConfig ||
39852                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39853         if(this.hideTrigger){
39854             this.trigger.setDisplayed(false);
39855         }
39856         this.initTrigger();
39857         if(!this.width){
39858             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39859         }
39860     },
39861
39862     // private
39863     initTrigger : function(){
39864         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39865         this.trigger.addClassOnOver('x-form-trigger-over');
39866         this.trigger.addClassOnClick('x-form-trigger-click');
39867     },
39868
39869     // private
39870     onDestroy : function(){
39871         if(this.trigger){
39872             this.trigger.removeAllListeners();
39873             this.trigger.remove();
39874         }
39875         if(this.wrap){
39876             this.wrap.remove();
39877         }
39878         Roo.form.TriggerField.superclass.onDestroy.call(this);
39879     },
39880
39881     // private
39882     onFocus : function(){
39883         Roo.form.TriggerField.superclass.onFocus.call(this);
39884         if(!this.mimicing){
39885             this.wrap.addClass('x-trigger-wrap-focus');
39886             this.mimicing = true;
39887             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39888             if(this.monitorTab){
39889                 this.el.on("keydown", this.checkTab, this);
39890             }
39891         }
39892     },
39893
39894     // private
39895     checkTab : function(e){
39896         if(e.getKey() == e.TAB){
39897             this.triggerBlur();
39898         }
39899     },
39900
39901     // private
39902     onBlur : function(){
39903         // do nothing
39904     },
39905
39906     // private
39907     mimicBlur : function(e, t){
39908         if(!this.wrap.contains(t) && this.validateBlur()){
39909             this.triggerBlur();
39910         }
39911     },
39912
39913     // private
39914     triggerBlur : function(){
39915         this.mimicing = false;
39916         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39917         if(this.monitorTab){
39918             this.el.un("keydown", this.checkTab, this);
39919         }
39920         this.wrap.removeClass('x-trigger-wrap-focus');
39921         Roo.form.TriggerField.superclass.onBlur.call(this);
39922     },
39923
39924     // private
39925     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39926     validateBlur : function(e, t){
39927         return true;
39928     },
39929
39930     // private
39931     onDisable : function(){
39932         Roo.form.TriggerField.superclass.onDisable.call(this);
39933         if(this.wrap){
39934             this.wrap.addClass('x-item-disabled');
39935         }
39936     },
39937
39938     // private
39939     onEnable : function(){
39940         Roo.form.TriggerField.superclass.onEnable.call(this);
39941         if(this.wrap){
39942             this.wrap.removeClass('x-item-disabled');
39943         }
39944     },
39945
39946     // private
39947     onShow : function(){
39948         var ae = this.getActionEl();
39949         
39950         if(ae){
39951             ae.dom.style.display = '';
39952             ae.dom.style.visibility = 'visible';
39953         }
39954     },
39955
39956     // private
39957     
39958     onHide : function(){
39959         var ae = this.getActionEl();
39960         ae.dom.style.display = 'none';
39961     },
39962
39963     /**
39964      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39965      * by an implementing function.
39966      * @method
39967      * @param {EventObject} e
39968      */
39969     onTriggerClick : Roo.emptyFn
39970 });
39971
39972 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39973 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39974 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39975 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39976     initComponent : function(){
39977         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39978
39979         this.triggerConfig = {
39980             tag:'span', cls:'x-form-twin-triggers', cn:[
39981             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39982             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39983         ]};
39984     },
39985
39986     getTrigger : function(index){
39987         return this.triggers[index];
39988     },
39989
39990     initTrigger : function(){
39991         var ts = this.trigger.select('.x-form-trigger', true);
39992         this.wrap.setStyle('overflow', 'hidden');
39993         var triggerField = this;
39994         ts.each(function(t, all, index){
39995             t.hide = function(){
39996                 var w = triggerField.wrap.getWidth();
39997                 this.dom.style.display = 'none';
39998                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39999             };
40000             t.show = function(){
40001                 var w = triggerField.wrap.getWidth();
40002                 this.dom.style.display = '';
40003                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40004             };
40005             var triggerIndex = 'Trigger'+(index+1);
40006
40007             if(this['hide'+triggerIndex]){
40008                 t.dom.style.display = 'none';
40009             }
40010             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40011             t.addClassOnOver('x-form-trigger-over');
40012             t.addClassOnClick('x-form-trigger-click');
40013         }, this);
40014         this.triggers = ts.elements;
40015     },
40016
40017     onTrigger1Click : Roo.emptyFn,
40018     onTrigger2Click : Roo.emptyFn
40019 });/*
40020  * Based on:
40021  * Ext JS Library 1.1.1
40022  * Copyright(c) 2006-2007, Ext JS, LLC.
40023  *
40024  * Originally Released Under LGPL - original licence link has changed is not relivant.
40025  *
40026  * Fork - LGPL
40027  * <script type="text/javascript">
40028  */
40029  
40030 /**
40031  * @class Roo.form.TextArea
40032  * @extends Roo.form.TextField
40033  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40034  * support for auto-sizing.
40035  * @constructor
40036  * Creates a new TextArea
40037  * @param {Object} config Configuration options
40038  */
40039 Roo.form.TextArea = function(config){
40040     Roo.form.TextArea.superclass.constructor.call(this, config);
40041     // these are provided exchanges for backwards compat
40042     // minHeight/maxHeight were replaced by growMin/growMax to be
40043     // compatible with TextField growing config values
40044     if(this.minHeight !== undefined){
40045         this.growMin = this.minHeight;
40046     }
40047     if(this.maxHeight !== undefined){
40048         this.growMax = this.maxHeight;
40049     }
40050 };
40051
40052 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40053     /**
40054      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40055      */
40056     growMin : 60,
40057     /**
40058      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40059      */
40060     growMax: 1000,
40061     /**
40062      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40063      * in the field (equivalent to setting overflow: hidden, defaults to false)
40064      */
40065     preventScrollbars: false,
40066     /**
40067      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40068      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40069      */
40070
40071     // private
40072     onRender : function(ct, position){
40073         if(!this.el){
40074             this.defaultAutoCreate = {
40075                 tag: "textarea",
40076                 style:"width:300px;height:60px;",
40077                 autocomplete: "new-password"
40078             };
40079         }
40080         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40081         if(this.grow){
40082             this.textSizeEl = Roo.DomHelper.append(document.body, {
40083                 tag: "pre", cls: "x-form-grow-sizer"
40084             });
40085             if(this.preventScrollbars){
40086                 this.el.setStyle("overflow", "hidden");
40087             }
40088             this.el.setHeight(this.growMin);
40089         }
40090     },
40091
40092     onDestroy : function(){
40093         if(this.textSizeEl){
40094             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40095         }
40096         Roo.form.TextArea.superclass.onDestroy.call(this);
40097     },
40098
40099     // private
40100     onKeyUp : function(e){
40101         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40102             this.autoSize();
40103         }
40104     },
40105
40106     /**
40107      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40108      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40109      */
40110     autoSize : function(){
40111         if(!this.grow || !this.textSizeEl){
40112             return;
40113         }
40114         var el = this.el;
40115         var v = el.dom.value;
40116         var ts = this.textSizeEl;
40117
40118         ts.innerHTML = '';
40119         ts.appendChild(document.createTextNode(v));
40120         v = ts.innerHTML;
40121
40122         Roo.fly(ts).setWidth(this.el.getWidth());
40123         if(v.length < 1){
40124             v = "&#160;&#160;";
40125         }else{
40126             if(Roo.isIE){
40127                 v = v.replace(/\n/g, '<p>&#160;</p>');
40128             }
40129             v += "&#160;\n&#160;";
40130         }
40131         ts.innerHTML = v;
40132         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40133         if(h != this.lastHeight){
40134             this.lastHeight = h;
40135             this.el.setHeight(h);
40136             this.fireEvent("autosize", this, h);
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 /**
40152  * @class Roo.form.NumberField
40153  * @extends Roo.form.TextField
40154  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40155  * @constructor
40156  * Creates a new NumberField
40157  * @param {Object} config Configuration options
40158  */
40159 Roo.form.NumberField = function(config){
40160     Roo.form.NumberField.superclass.constructor.call(this, config);
40161 };
40162
40163 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40164     /**
40165      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40166      */
40167     fieldClass: "x-form-field x-form-num-field",
40168     /**
40169      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40170      */
40171     allowDecimals : true,
40172     /**
40173      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40174      */
40175     decimalSeparator : ".",
40176     /**
40177      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40178      */
40179     decimalPrecision : 2,
40180     /**
40181      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40182      */
40183     allowNegative : true,
40184     /**
40185      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40186      */
40187     minValue : Number.NEGATIVE_INFINITY,
40188     /**
40189      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40190      */
40191     maxValue : Number.MAX_VALUE,
40192     /**
40193      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40194      */
40195     minText : "The minimum value for this field is {0}",
40196     /**
40197      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40198      */
40199     maxText : "The maximum value for this field is {0}",
40200     /**
40201      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40202      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40203      */
40204     nanText : "{0} is not a valid number",
40205
40206     // private
40207     initEvents : function(){
40208         Roo.form.NumberField.superclass.initEvents.call(this);
40209         var allowed = "0123456789";
40210         if(this.allowDecimals){
40211             allowed += this.decimalSeparator;
40212         }
40213         if(this.allowNegative){
40214             allowed += "-";
40215         }
40216         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40217         var keyPress = function(e){
40218             var k = e.getKey();
40219             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40220                 return;
40221             }
40222             var c = e.getCharCode();
40223             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40224                 e.stopEvent();
40225             }
40226         };
40227         this.el.on("keypress", keyPress, this);
40228     },
40229
40230     // private
40231     validateValue : function(value){
40232         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40233             return false;
40234         }
40235         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40236              return true;
40237         }
40238         var num = this.parseValue(value);
40239         if(isNaN(num)){
40240             this.markInvalid(String.format(this.nanText, value));
40241             return false;
40242         }
40243         if(num < this.minValue){
40244             this.markInvalid(String.format(this.minText, this.minValue));
40245             return false;
40246         }
40247         if(num > this.maxValue){
40248             this.markInvalid(String.format(this.maxText, this.maxValue));
40249             return false;
40250         }
40251         return true;
40252     },
40253
40254     getValue : function(){
40255         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40256     },
40257
40258     // private
40259     parseValue : function(value){
40260         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40261         return isNaN(value) ? '' : value;
40262     },
40263
40264     // private
40265     fixPrecision : function(value){
40266         var nan = isNaN(value);
40267         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40268             return nan ? '' : value;
40269         }
40270         return parseFloat(value).toFixed(this.decimalPrecision);
40271     },
40272
40273     setValue : function(v){
40274         v = this.fixPrecision(v);
40275         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40276     },
40277
40278     // private
40279     decimalPrecisionFcn : function(v){
40280         return Math.floor(v);
40281     },
40282
40283     beforeBlur : function(){
40284         var v = this.parseValue(this.getRawValue());
40285         if(v){
40286             this.setValue(v);
40287         }
40288     }
40289 });/*
40290  * Based on:
40291  * Ext JS Library 1.1.1
40292  * Copyright(c) 2006-2007, Ext JS, LLC.
40293  *
40294  * Originally Released Under LGPL - original licence link has changed is not relivant.
40295  *
40296  * Fork - LGPL
40297  * <script type="text/javascript">
40298  */
40299  
40300 /**
40301  * @class Roo.form.DateField
40302  * @extends Roo.form.TriggerField
40303  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40304 * @constructor
40305 * Create a new DateField
40306 * @param {Object} config
40307  */
40308 Roo.form.DateField = function(config)
40309 {
40310     Roo.form.DateField.superclass.constructor.call(this, config);
40311     
40312       this.addEvents({
40313          
40314         /**
40315          * @event select
40316          * Fires when a date is selected
40317              * @param {Roo.form.DateField} combo This combo box
40318              * @param {Date} date The date selected
40319              */
40320         'select' : true
40321          
40322     });
40323     
40324     
40325     if(typeof this.minValue == "string") {
40326         this.minValue = this.parseDate(this.minValue);
40327     }
40328     if(typeof this.maxValue == "string") {
40329         this.maxValue = this.parseDate(this.maxValue);
40330     }
40331     this.ddMatch = null;
40332     if(this.disabledDates){
40333         var dd = this.disabledDates;
40334         var re = "(?:";
40335         for(var i = 0; i < dd.length; i++){
40336             re += dd[i];
40337             if(i != dd.length-1) {
40338                 re += "|";
40339             }
40340         }
40341         this.ddMatch = new RegExp(re + ")");
40342     }
40343 };
40344
40345 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40346     /**
40347      * @cfg {String} format
40348      * The default date format string which can be overriden for localization support.  The format must be
40349      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40350      */
40351     format : "m/d/y",
40352     /**
40353      * @cfg {String} altFormats
40354      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40355      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40356      */
40357     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40358     /**
40359      * @cfg {Array} disabledDays
40360      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40361      */
40362     disabledDays : null,
40363     /**
40364      * @cfg {String} disabledDaysText
40365      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40366      */
40367     disabledDaysText : "Disabled",
40368     /**
40369      * @cfg {Array} disabledDates
40370      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40371      * expression so they are very powerful. Some examples:
40372      * <ul>
40373      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40374      * <li>["03/08", "09/16"] would disable those days for every year</li>
40375      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40376      * <li>["03/../2006"] would disable every day in March 2006</li>
40377      * <li>["^03"] would disable every day in every March</li>
40378      * </ul>
40379      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40380      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40381      */
40382     disabledDates : null,
40383     /**
40384      * @cfg {String} disabledDatesText
40385      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40386      */
40387     disabledDatesText : "Disabled",
40388     /**
40389      * @cfg {Date/String} minValue
40390      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40391      * valid format (defaults to null).
40392      */
40393     minValue : null,
40394     /**
40395      * @cfg {Date/String} maxValue
40396      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40397      * valid format (defaults to null).
40398      */
40399     maxValue : null,
40400     /**
40401      * @cfg {String} minText
40402      * The error text to display when the date in the cell is before minValue (defaults to
40403      * 'The date in this field must be after {minValue}').
40404      */
40405     minText : "The date in this field must be equal to or after {0}",
40406     /**
40407      * @cfg {String} maxText
40408      * The error text to display when the date in the cell is after maxValue (defaults to
40409      * 'The date in this field must be before {maxValue}').
40410      */
40411     maxText : "The date in this field must be equal to or before {0}",
40412     /**
40413      * @cfg {String} invalidText
40414      * The error text to display when the date in the field is invalid (defaults to
40415      * '{value} is not a valid date - it must be in the format {format}').
40416      */
40417     invalidText : "{0} is not a valid date - it must be in the format {1}",
40418     /**
40419      * @cfg {String} triggerClass
40420      * An additional CSS class used to style the trigger button.  The trigger will always get the
40421      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40422      * which displays a calendar icon).
40423      */
40424     triggerClass : 'x-form-date-trigger',
40425     
40426
40427     /**
40428      * @cfg {Boolean} useIso
40429      * if enabled, then the date field will use a hidden field to store the 
40430      * real value as iso formated date. default (false)
40431      */ 
40432     useIso : false,
40433     /**
40434      * @cfg {String/Object} autoCreate
40435      * A DomHelper element spec, or true for a default element spec (defaults to
40436      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40437      */ 
40438     // private
40439     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40440     
40441     // private
40442     hiddenField: false,
40443     
40444     onRender : function(ct, position)
40445     {
40446         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40447         if (this.useIso) {
40448             //this.el.dom.removeAttribute('name'); 
40449             Roo.log("Changing name?");
40450             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40451             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40452                     'before', true);
40453             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40454             // prevent input submission
40455             this.hiddenName = this.name;
40456         }
40457             
40458             
40459     },
40460     
40461     // private
40462     validateValue : function(value)
40463     {
40464         value = this.formatDate(value);
40465         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40466             Roo.log('super failed');
40467             return false;
40468         }
40469         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40470              return true;
40471         }
40472         var svalue = value;
40473         value = this.parseDate(value);
40474         if(!value){
40475             Roo.log('parse date failed' + svalue);
40476             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40477             return false;
40478         }
40479         var time = value.getTime();
40480         if(this.minValue && time < this.minValue.getTime()){
40481             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40482             return false;
40483         }
40484         if(this.maxValue && time > this.maxValue.getTime()){
40485             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40486             return false;
40487         }
40488         if(this.disabledDays){
40489             var day = value.getDay();
40490             for(var i = 0; i < this.disabledDays.length; i++) {
40491                 if(day === this.disabledDays[i]){
40492                     this.markInvalid(this.disabledDaysText);
40493                     return false;
40494                 }
40495             }
40496         }
40497         var fvalue = this.formatDate(value);
40498         if(this.ddMatch && this.ddMatch.test(fvalue)){
40499             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40500             return false;
40501         }
40502         return true;
40503     },
40504
40505     // private
40506     // Provides logic to override the default TriggerField.validateBlur which just returns true
40507     validateBlur : function(){
40508         return !this.menu || !this.menu.isVisible();
40509     },
40510     
40511     getName: function()
40512     {
40513         // returns hidden if it's set..
40514         if (!this.rendered) {return ''};
40515         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40516         
40517     },
40518
40519     /**
40520      * Returns the current date value of the date field.
40521      * @return {Date} The date value
40522      */
40523     getValue : function(){
40524         
40525         return  this.hiddenField ?
40526                 this.hiddenField.value :
40527                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40528     },
40529
40530     /**
40531      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40532      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40533      * (the default format used is "m/d/y").
40534      * <br />Usage:
40535      * <pre><code>
40536 //All of these calls set the same date value (May 4, 2006)
40537
40538 //Pass a date object:
40539 var dt = new Date('5/4/06');
40540 dateField.setValue(dt);
40541
40542 //Pass a date string (default format):
40543 dateField.setValue('5/4/06');
40544
40545 //Pass a date string (custom format):
40546 dateField.format = 'Y-m-d';
40547 dateField.setValue('2006-5-4');
40548 </code></pre>
40549      * @param {String/Date} date The date or valid date string
40550      */
40551     setValue : function(date){
40552         if (this.hiddenField) {
40553             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40554         }
40555         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40556         // make sure the value field is always stored as a date..
40557         this.value = this.parseDate(date);
40558         
40559         
40560     },
40561
40562     // private
40563     parseDate : function(value){
40564         if(!value || value instanceof Date){
40565             return value;
40566         }
40567         var v = Date.parseDate(value, this.format);
40568          if (!v && this.useIso) {
40569             v = Date.parseDate(value, 'Y-m-d');
40570         }
40571         if(!v && this.altFormats){
40572             if(!this.altFormatsArray){
40573                 this.altFormatsArray = this.altFormats.split("|");
40574             }
40575             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40576                 v = Date.parseDate(value, this.altFormatsArray[i]);
40577             }
40578         }
40579         return v;
40580     },
40581
40582     // private
40583     formatDate : function(date, fmt){
40584         return (!date || !(date instanceof Date)) ?
40585                date : date.dateFormat(fmt || this.format);
40586     },
40587
40588     // private
40589     menuListeners : {
40590         select: function(m, d){
40591             
40592             this.setValue(d);
40593             this.fireEvent('select', this, d);
40594         },
40595         show : function(){ // retain focus styling
40596             this.onFocus();
40597         },
40598         hide : function(){
40599             this.focus.defer(10, this);
40600             var ml = this.menuListeners;
40601             this.menu.un("select", ml.select,  this);
40602             this.menu.un("show", ml.show,  this);
40603             this.menu.un("hide", ml.hide,  this);
40604         }
40605     },
40606
40607     // private
40608     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40609     onTriggerClick : function(){
40610         if(this.disabled){
40611             return;
40612         }
40613         if(this.menu == null){
40614             this.menu = new Roo.menu.DateMenu();
40615         }
40616         Roo.apply(this.menu.picker,  {
40617             showClear: this.allowBlank,
40618             minDate : this.minValue,
40619             maxDate : this.maxValue,
40620             disabledDatesRE : this.ddMatch,
40621             disabledDatesText : this.disabledDatesText,
40622             disabledDays : this.disabledDays,
40623             disabledDaysText : this.disabledDaysText,
40624             format : this.useIso ? 'Y-m-d' : this.format,
40625             minText : String.format(this.minText, this.formatDate(this.minValue)),
40626             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40627         });
40628         this.menu.on(Roo.apply({}, this.menuListeners, {
40629             scope:this
40630         }));
40631         this.menu.picker.setValue(this.getValue() || new Date());
40632         this.menu.show(this.el, "tl-bl?");
40633     },
40634
40635     beforeBlur : function(){
40636         var v = this.parseDate(this.getRawValue());
40637         if(v){
40638             this.setValue(v);
40639         }
40640     },
40641
40642     /*@
40643      * overide
40644      * 
40645      */
40646     isDirty : function() {
40647         if(this.disabled) {
40648             return false;
40649         }
40650         
40651         if(typeof(this.startValue) === 'undefined'){
40652             return false;
40653         }
40654         
40655         return String(this.getValue()) !== String(this.startValue);
40656         
40657     },
40658     // @overide
40659     cleanLeadingSpace : function(e)
40660     {
40661        return;
40662     }
40663     
40664 });/*
40665  * Based on:
40666  * Ext JS Library 1.1.1
40667  * Copyright(c) 2006-2007, Ext JS, LLC.
40668  *
40669  * Originally Released Under LGPL - original licence link has changed is not relivant.
40670  *
40671  * Fork - LGPL
40672  * <script type="text/javascript">
40673  */
40674  
40675 /**
40676  * @class Roo.form.MonthField
40677  * @extends Roo.form.TriggerField
40678  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40679 * @constructor
40680 * Create a new MonthField
40681 * @param {Object} config
40682  */
40683 Roo.form.MonthField = function(config){
40684     
40685     Roo.form.MonthField.superclass.constructor.call(this, config);
40686     
40687       this.addEvents({
40688          
40689         /**
40690          * @event select
40691          * Fires when a date is selected
40692              * @param {Roo.form.MonthFieeld} combo This combo box
40693              * @param {Date} date The date selected
40694              */
40695         'select' : true
40696          
40697     });
40698     
40699     
40700     if(typeof this.minValue == "string") {
40701         this.minValue = this.parseDate(this.minValue);
40702     }
40703     if(typeof this.maxValue == "string") {
40704         this.maxValue = this.parseDate(this.maxValue);
40705     }
40706     this.ddMatch = null;
40707     if(this.disabledDates){
40708         var dd = this.disabledDates;
40709         var re = "(?:";
40710         for(var i = 0; i < dd.length; i++){
40711             re += dd[i];
40712             if(i != dd.length-1) {
40713                 re += "|";
40714             }
40715         }
40716         this.ddMatch = new RegExp(re + ")");
40717     }
40718 };
40719
40720 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40721     /**
40722      * @cfg {String} format
40723      * The default date format string which can be overriden for localization support.  The format must be
40724      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40725      */
40726     format : "M Y",
40727     /**
40728      * @cfg {String} altFormats
40729      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40730      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40731      */
40732     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40733     /**
40734      * @cfg {Array} disabledDays
40735      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40736      */
40737     disabledDays : [0,1,2,3,4,5,6],
40738     /**
40739      * @cfg {String} disabledDaysText
40740      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40741      */
40742     disabledDaysText : "Disabled",
40743     /**
40744      * @cfg {Array} disabledDates
40745      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40746      * expression so they are very powerful. Some examples:
40747      * <ul>
40748      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40749      * <li>["03/08", "09/16"] would disable those days for every year</li>
40750      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40751      * <li>["03/../2006"] would disable every day in March 2006</li>
40752      * <li>["^03"] would disable every day in every March</li>
40753      * </ul>
40754      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40755      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40756      */
40757     disabledDates : null,
40758     /**
40759      * @cfg {String} disabledDatesText
40760      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40761      */
40762     disabledDatesText : "Disabled",
40763     /**
40764      * @cfg {Date/String} minValue
40765      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40766      * valid format (defaults to null).
40767      */
40768     minValue : null,
40769     /**
40770      * @cfg {Date/String} maxValue
40771      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40772      * valid format (defaults to null).
40773      */
40774     maxValue : null,
40775     /**
40776      * @cfg {String} minText
40777      * The error text to display when the date in the cell is before minValue (defaults to
40778      * 'The date in this field must be after {minValue}').
40779      */
40780     minText : "The date in this field must be equal to or after {0}",
40781     /**
40782      * @cfg {String} maxTextf
40783      * The error text to display when the date in the cell is after maxValue (defaults to
40784      * 'The date in this field must be before {maxValue}').
40785      */
40786     maxText : "The date in this field must be equal to or before {0}",
40787     /**
40788      * @cfg {String} invalidText
40789      * The error text to display when the date in the field is invalid (defaults to
40790      * '{value} is not a valid date - it must be in the format {format}').
40791      */
40792     invalidText : "{0} is not a valid date - it must be in the format {1}",
40793     /**
40794      * @cfg {String} triggerClass
40795      * An additional CSS class used to style the trigger button.  The trigger will always get the
40796      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40797      * which displays a calendar icon).
40798      */
40799     triggerClass : 'x-form-date-trigger',
40800     
40801
40802     /**
40803      * @cfg {Boolean} useIso
40804      * if enabled, then the date field will use a hidden field to store the 
40805      * real value as iso formated date. default (true)
40806      */ 
40807     useIso : true,
40808     /**
40809      * @cfg {String/Object} autoCreate
40810      * A DomHelper element spec, or true for a default element spec (defaults to
40811      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40812      */ 
40813     // private
40814     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40815     
40816     // private
40817     hiddenField: false,
40818     
40819     hideMonthPicker : false,
40820     
40821     onRender : function(ct, position)
40822     {
40823         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40824         if (this.useIso) {
40825             this.el.dom.removeAttribute('name'); 
40826             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40827                     'before', true);
40828             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40829             // prevent input submission
40830             this.hiddenName = this.name;
40831         }
40832             
40833             
40834     },
40835     
40836     // private
40837     validateValue : function(value)
40838     {
40839         value = this.formatDate(value);
40840         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40841             return false;
40842         }
40843         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40844              return true;
40845         }
40846         var svalue = value;
40847         value = this.parseDate(value);
40848         if(!value){
40849             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40850             return false;
40851         }
40852         var time = value.getTime();
40853         if(this.minValue && time < this.minValue.getTime()){
40854             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40855             return false;
40856         }
40857         if(this.maxValue && time > this.maxValue.getTime()){
40858             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40859             return false;
40860         }
40861         /*if(this.disabledDays){
40862             var day = value.getDay();
40863             for(var i = 0; i < this.disabledDays.length; i++) {
40864                 if(day === this.disabledDays[i]){
40865                     this.markInvalid(this.disabledDaysText);
40866                     return false;
40867                 }
40868             }
40869         }
40870         */
40871         var fvalue = this.formatDate(value);
40872         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40873             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40874             return false;
40875         }
40876         */
40877         return true;
40878     },
40879
40880     // private
40881     // Provides logic to override the default TriggerField.validateBlur which just returns true
40882     validateBlur : function(){
40883         return !this.menu || !this.menu.isVisible();
40884     },
40885
40886     /**
40887      * Returns the current date value of the date field.
40888      * @return {Date} The date value
40889      */
40890     getValue : function(){
40891         
40892         
40893         
40894         return  this.hiddenField ?
40895                 this.hiddenField.value :
40896                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40897     },
40898
40899     /**
40900      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40901      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40902      * (the default format used is "m/d/y").
40903      * <br />Usage:
40904      * <pre><code>
40905 //All of these calls set the same date value (May 4, 2006)
40906
40907 //Pass a date object:
40908 var dt = new Date('5/4/06');
40909 monthField.setValue(dt);
40910
40911 //Pass a date string (default format):
40912 monthField.setValue('5/4/06');
40913
40914 //Pass a date string (custom format):
40915 monthField.format = 'Y-m-d';
40916 monthField.setValue('2006-5-4');
40917 </code></pre>
40918      * @param {String/Date} date The date or valid date string
40919      */
40920     setValue : function(date){
40921         Roo.log('month setValue' + date);
40922         // can only be first of month..
40923         
40924         var val = this.parseDate(date);
40925         
40926         if (this.hiddenField) {
40927             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40928         }
40929         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40930         this.value = this.parseDate(date);
40931     },
40932
40933     // private
40934     parseDate : function(value){
40935         if(!value || value instanceof Date){
40936             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40937             return value;
40938         }
40939         var v = Date.parseDate(value, this.format);
40940         if (!v && this.useIso) {
40941             v = Date.parseDate(value, 'Y-m-d');
40942         }
40943         if (v) {
40944             // 
40945             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40946         }
40947         
40948         
40949         if(!v && this.altFormats){
40950             if(!this.altFormatsArray){
40951                 this.altFormatsArray = this.altFormats.split("|");
40952             }
40953             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40954                 v = Date.parseDate(value, this.altFormatsArray[i]);
40955             }
40956         }
40957         return v;
40958     },
40959
40960     // private
40961     formatDate : function(date, fmt){
40962         return (!date || !(date instanceof Date)) ?
40963                date : date.dateFormat(fmt || this.format);
40964     },
40965
40966     // private
40967     menuListeners : {
40968         select: function(m, d){
40969             this.setValue(d);
40970             this.fireEvent('select', this, d);
40971         },
40972         show : function(){ // retain focus styling
40973             this.onFocus();
40974         },
40975         hide : function(){
40976             this.focus.defer(10, this);
40977             var ml = this.menuListeners;
40978             this.menu.un("select", ml.select,  this);
40979             this.menu.un("show", ml.show,  this);
40980             this.menu.un("hide", ml.hide,  this);
40981         }
40982     },
40983     // private
40984     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40985     onTriggerClick : function(){
40986         if(this.disabled){
40987             return;
40988         }
40989         if(this.menu == null){
40990             this.menu = new Roo.menu.DateMenu();
40991            
40992         }
40993         
40994         Roo.apply(this.menu.picker,  {
40995             
40996             showClear: this.allowBlank,
40997             minDate : this.minValue,
40998             maxDate : this.maxValue,
40999             disabledDatesRE : this.ddMatch,
41000             disabledDatesText : this.disabledDatesText,
41001             
41002             format : this.useIso ? 'Y-m-d' : this.format,
41003             minText : String.format(this.minText, this.formatDate(this.minValue)),
41004             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41005             
41006         });
41007          this.menu.on(Roo.apply({}, this.menuListeners, {
41008             scope:this
41009         }));
41010        
41011         
41012         var m = this.menu;
41013         var p = m.picker;
41014         
41015         // hide month picker get's called when we called by 'before hide';
41016         
41017         var ignorehide = true;
41018         p.hideMonthPicker  = function(disableAnim){
41019             if (ignorehide) {
41020                 return;
41021             }
41022              if(this.monthPicker){
41023                 Roo.log("hideMonthPicker called");
41024                 if(disableAnim === true){
41025                     this.monthPicker.hide();
41026                 }else{
41027                     this.monthPicker.slideOut('t', {duration:.2});
41028                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41029                     p.fireEvent("select", this, this.value);
41030                     m.hide();
41031                 }
41032             }
41033         }
41034         
41035         Roo.log('picker set value');
41036         Roo.log(this.getValue());
41037         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41038         m.show(this.el, 'tl-bl?');
41039         ignorehide  = false;
41040         // this will trigger hideMonthPicker..
41041         
41042         
41043         // hidden the day picker
41044         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41045         
41046         
41047         
41048       
41049         
41050         p.showMonthPicker.defer(100, p);
41051     
41052         
41053        
41054     },
41055
41056     beforeBlur : function(){
41057         var v = this.parseDate(this.getRawValue());
41058         if(v){
41059             this.setValue(v);
41060         }
41061     }
41062
41063     /** @cfg {Boolean} grow @hide */
41064     /** @cfg {Number} growMin @hide */
41065     /** @cfg {Number} growMax @hide */
41066     /**
41067      * @hide
41068      * @method autoSize
41069      */
41070 });/*
41071  * Based on:
41072  * Ext JS Library 1.1.1
41073  * Copyright(c) 2006-2007, Ext JS, LLC.
41074  *
41075  * Originally Released Under LGPL - original licence link has changed is not relivant.
41076  *
41077  * Fork - LGPL
41078  * <script type="text/javascript">
41079  */
41080  
41081
41082 /**
41083  * @class Roo.form.ComboBox
41084  * @extends Roo.form.TriggerField
41085  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41086  * @constructor
41087  * Create a new ComboBox.
41088  * @param {Object} config Configuration options
41089  */
41090 Roo.form.ComboBox = function(config){
41091     Roo.form.ComboBox.superclass.constructor.call(this, config);
41092     this.addEvents({
41093         /**
41094          * @event expand
41095          * Fires when the dropdown list is expanded
41096              * @param {Roo.form.ComboBox} combo This combo box
41097              */
41098         'expand' : true,
41099         /**
41100          * @event collapse
41101          * Fires when the dropdown list is collapsed
41102              * @param {Roo.form.ComboBox} combo This combo box
41103              */
41104         'collapse' : true,
41105         /**
41106          * @event beforeselect
41107          * Fires before a list item is selected. Return false to cancel the selection.
41108              * @param {Roo.form.ComboBox} combo This combo box
41109              * @param {Roo.data.Record} record The data record returned from the underlying store
41110              * @param {Number} index The index of the selected item in the dropdown list
41111              */
41112         'beforeselect' : true,
41113         /**
41114          * @event select
41115          * Fires when a list item is selected
41116              * @param {Roo.form.ComboBox} combo This combo box
41117              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41118              * @param {Number} index The index of the selected item in the dropdown list
41119              */
41120         'select' : true,
41121         /**
41122          * @event beforequery
41123          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41124          * The event object passed has these properties:
41125              * @param {Roo.form.ComboBox} combo This combo box
41126              * @param {String} query The query
41127              * @param {Boolean} forceAll true to force "all" query
41128              * @param {Boolean} cancel true to cancel the query
41129              * @param {Object} e The query event object
41130              */
41131         'beforequery': true,
41132          /**
41133          * @event add
41134          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41135              * @param {Roo.form.ComboBox} combo This combo box
41136              */
41137         'add' : true,
41138         /**
41139          * @event edit
41140          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41141              * @param {Roo.form.ComboBox} combo This combo box
41142              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41143              */
41144         'edit' : true
41145         
41146         
41147     });
41148     if(this.transform){
41149         this.allowDomMove = false;
41150         var s = Roo.getDom(this.transform);
41151         if(!this.hiddenName){
41152             this.hiddenName = s.name;
41153         }
41154         if(!this.store){
41155             this.mode = 'local';
41156             var d = [], opts = s.options;
41157             for(var i = 0, len = opts.length;i < len; i++){
41158                 var o = opts[i];
41159                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41160                 if(o.selected) {
41161                     this.value = value;
41162                 }
41163                 d.push([value, o.text]);
41164             }
41165             this.store = new Roo.data.SimpleStore({
41166                 'id': 0,
41167                 fields: ['value', 'text'],
41168                 data : d
41169             });
41170             this.valueField = 'value';
41171             this.displayField = 'text';
41172         }
41173         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41174         if(!this.lazyRender){
41175             this.target = true;
41176             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41177             s.parentNode.removeChild(s); // remove it
41178             this.render(this.el.parentNode);
41179         }else{
41180             s.parentNode.removeChild(s); // remove it
41181         }
41182
41183     }
41184     if (this.store) {
41185         this.store = Roo.factory(this.store, Roo.data);
41186     }
41187     
41188     this.selectedIndex = -1;
41189     if(this.mode == 'local'){
41190         if(config.queryDelay === undefined){
41191             this.queryDelay = 10;
41192         }
41193         if(config.minChars === undefined){
41194             this.minChars = 0;
41195         }
41196     }
41197 };
41198
41199 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41200     /**
41201      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41202      */
41203     /**
41204      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41205      * rendering into an Roo.Editor, defaults to false)
41206      */
41207     /**
41208      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41209      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41210      */
41211     /**
41212      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41213      */
41214     /**
41215      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41216      * the dropdown list (defaults to undefined, with no header element)
41217      */
41218
41219      /**
41220      * @cfg {String/Roo.Template} tpl The template to use to render the output
41221      */
41222      
41223     // private
41224     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41225     /**
41226      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41227      */
41228     listWidth: undefined,
41229     /**
41230      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41231      * mode = 'remote' or 'text' if mode = 'local')
41232      */
41233     displayField: undefined,
41234     /**
41235      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41236      * mode = 'remote' or 'value' if mode = 'local'). 
41237      * Note: use of a valueField requires the user make a selection
41238      * in order for a value to be mapped.
41239      */
41240     valueField: undefined,
41241     
41242     
41243     /**
41244      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41245      * field's data value (defaults to the underlying DOM element's name)
41246      */
41247     hiddenName: undefined,
41248     /**
41249      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41250      */
41251     listClass: '',
41252     /**
41253      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41254      */
41255     selectedClass: 'x-combo-selected',
41256     /**
41257      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41258      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41259      * which displays a downward arrow icon).
41260      */
41261     triggerClass : 'x-form-arrow-trigger',
41262     /**
41263      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41264      */
41265     shadow:'sides',
41266     /**
41267      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41268      * anchor positions (defaults to 'tl-bl')
41269      */
41270     listAlign: 'tl-bl?',
41271     /**
41272      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41273      */
41274     maxHeight: 300,
41275     /**
41276      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41277      * query specified by the allQuery config option (defaults to 'query')
41278      */
41279     triggerAction: 'query',
41280     /**
41281      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41282      * (defaults to 4, does not apply if editable = false)
41283      */
41284     minChars : 4,
41285     /**
41286      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41287      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41288      */
41289     typeAhead: false,
41290     /**
41291      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41292      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41293      */
41294     queryDelay: 500,
41295     /**
41296      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41297      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41298      */
41299     pageSize: 0,
41300     /**
41301      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41302      * when editable = true (defaults to false)
41303      */
41304     selectOnFocus:false,
41305     /**
41306      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41307      */
41308     queryParam: 'query',
41309     /**
41310      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41311      * when mode = 'remote' (defaults to 'Loading...')
41312      */
41313     loadingText: 'Loading...',
41314     /**
41315      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41316      */
41317     resizable: false,
41318     /**
41319      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41320      */
41321     handleHeight : 8,
41322     /**
41323      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41324      * traditional select (defaults to true)
41325      */
41326     editable: true,
41327     /**
41328      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41329      */
41330     allQuery: '',
41331     /**
41332      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41333      */
41334     mode: 'remote',
41335     /**
41336      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41337      * listWidth has a higher value)
41338      */
41339     minListWidth : 70,
41340     /**
41341      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41342      * allow the user to set arbitrary text into the field (defaults to false)
41343      */
41344     forceSelection:false,
41345     /**
41346      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41347      * if typeAhead = true (defaults to 250)
41348      */
41349     typeAheadDelay : 250,
41350     /**
41351      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41352      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41353      */
41354     valueNotFoundText : undefined,
41355     /**
41356      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41357      */
41358     blockFocus : false,
41359     
41360     /**
41361      * @cfg {Boolean} disableClear Disable showing of clear button.
41362      */
41363     disableClear : false,
41364     /**
41365      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41366      */
41367     alwaysQuery : false,
41368     
41369     //private
41370     addicon : false,
41371     editicon: false,
41372     
41373     // element that contains real text value.. (when hidden is used..)
41374      
41375     // private
41376     onRender : function(ct, position)
41377     {
41378         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41379         
41380         if(this.hiddenName){
41381             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41382                     'before', true);
41383             this.hiddenField.value =
41384                 this.hiddenValue !== undefined ? this.hiddenValue :
41385                 this.value !== undefined ? this.value : '';
41386
41387             // prevent input submission
41388             this.el.dom.removeAttribute('name');
41389              
41390              
41391         }
41392         
41393         if(Roo.isGecko){
41394             this.el.dom.setAttribute('autocomplete', 'off');
41395         }
41396
41397         var cls = 'x-combo-list';
41398
41399         this.list = new Roo.Layer({
41400             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41401         });
41402
41403         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41404         this.list.setWidth(lw);
41405         this.list.swallowEvent('mousewheel');
41406         this.assetHeight = 0;
41407
41408         if(this.title){
41409             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41410             this.assetHeight += this.header.getHeight();
41411         }
41412
41413         this.innerList = this.list.createChild({cls:cls+'-inner'});
41414         this.innerList.on('mouseover', this.onViewOver, this);
41415         this.innerList.on('mousemove', this.onViewMove, this);
41416         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41417         
41418         if(this.allowBlank && !this.pageSize && !this.disableClear){
41419             this.footer = this.list.createChild({cls:cls+'-ft'});
41420             this.pageTb = new Roo.Toolbar(this.footer);
41421            
41422         }
41423         if(this.pageSize){
41424             this.footer = this.list.createChild({cls:cls+'-ft'});
41425             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41426                     {pageSize: this.pageSize});
41427             
41428         }
41429         
41430         if (this.pageTb && this.allowBlank && !this.disableClear) {
41431             var _this = this;
41432             this.pageTb.add(new Roo.Toolbar.Fill(), {
41433                 cls: 'x-btn-icon x-btn-clear',
41434                 text: '&#160;',
41435                 handler: function()
41436                 {
41437                     _this.collapse();
41438                     _this.clearValue();
41439                     _this.onSelect(false, -1);
41440                 }
41441             });
41442         }
41443         if (this.footer) {
41444             this.assetHeight += this.footer.getHeight();
41445         }
41446         
41447
41448         if(!this.tpl){
41449             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41450         }
41451
41452         this.view = new Roo.View(this.innerList, this.tpl, {
41453             singleSelect:true,
41454             store: this.store,
41455             selectedClass: this.selectedClass
41456         });
41457
41458         this.view.on('click', this.onViewClick, this);
41459
41460         this.store.on('beforeload', this.onBeforeLoad, this);
41461         this.store.on('load', this.onLoad, this);
41462         this.store.on('loadexception', this.onLoadException, this);
41463
41464         if(this.resizable){
41465             this.resizer = new Roo.Resizable(this.list,  {
41466                pinned:true, handles:'se'
41467             });
41468             this.resizer.on('resize', function(r, w, h){
41469                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41470                 this.listWidth = w;
41471                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41472                 this.restrictHeight();
41473             }, this);
41474             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41475         }
41476         if(!this.editable){
41477             this.editable = true;
41478             this.setEditable(false);
41479         }  
41480         
41481         
41482         if (typeof(this.events.add.listeners) != 'undefined') {
41483             
41484             this.addicon = this.wrap.createChild(
41485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41486        
41487             this.addicon.on('click', function(e) {
41488                 this.fireEvent('add', this);
41489             }, this);
41490         }
41491         if (typeof(this.events.edit.listeners) != 'undefined') {
41492             
41493             this.editicon = this.wrap.createChild(
41494                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41495             if (this.addicon) {
41496                 this.editicon.setStyle('margin-left', '40px');
41497             }
41498             this.editicon.on('click', function(e) {
41499                 
41500                 // we fire even  if inothing is selected..
41501                 this.fireEvent('edit', this, this.lastData );
41502                 
41503             }, this);
41504         }
41505         
41506         
41507         
41508     },
41509
41510     // private
41511     initEvents : function(){
41512         Roo.form.ComboBox.superclass.initEvents.call(this);
41513
41514         this.keyNav = new Roo.KeyNav(this.el, {
41515             "up" : function(e){
41516                 this.inKeyMode = true;
41517                 this.selectPrev();
41518             },
41519
41520             "down" : function(e){
41521                 if(!this.isExpanded()){
41522                     this.onTriggerClick();
41523                 }else{
41524                     this.inKeyMode = true;
41525                     this.selectNext();
41526                 }
41527             },
41528
41529             "enter" : function(e){
41530                 this.onViewClick();
41531                 //return true;
41532             },
41533
41534             "esc" : function(e){
41535                 this.collapse();
41536             },
41537
41538             "tab" : function(e){
41539                 this.onViewClick(false);
41540                 this.fireEvent("specialkey", this, e);
41541                 return true;
41542             },
41543
41544             scope : this,
41545
41546             doRelay : function(foo, bar, hname){
41547                 if(hname == 'down' || this.scope.isExpanded()){
41548                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41549                 }
41550                 return true;
41551             },
41552
41553             forceKeyDown: true
41554         });
41555         this.queryDelay = Math.max(this.queryDelay || 10,
41556                 this.mode == 'local' ? 10 : 250);
41557         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41558         if(this.typeAhead){
41559             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41560         }
41561         if(this.editable !== false){
41562             this.el.on("keyup", this.onKeyUp, this);
41563         }
41564         if(this.forceSelection){
41565             this.on('blur', this.doForce, this);
41566         }
41567     },
41568
41569     onDestroy : function(){
41570         if(this.view){
41571             this.view.setStore(null);
41572             this.view.el.removeAllListeners();
41573             this.view.el.remove();
41574             this.view.purgeListeners();
41575         }
41576         if(this.list){
41577             this.list.destroy();
41578         }
41579         if(this.store){
41580             this.store.un('beforeload', this.onBeforeLoad, this);
41581             this.store.un('load', this.onLoad, this);
41582             this.store.un('loadexception', this.onLoadException, this);
41583         }
41584         Roo.form.ComboBox.superclass.onDestroy.call(this);
41585     },
41586
41587     // private
41588     fireKey : function(e){
41589         if(e.isNavKeyPress() && !this.list.isVisible()){
41590             this.fireEvent("specialkey", this, e);
41591         }
41592     },
41593
41594     // private
41595     onResize: function(w, h){
41596         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41597         
41598         if(typeof w != 'number'){
41599             // we do not handle it!?!?
41600             return;
41601         }
41602         var tw = this.trigger.getWidth();
41603         tw += this.addicon ? this.addicon.getWidth() : 0;
41604         tw += this.editicon ? this.editicon.getWidth() : 0;
41605         var x = w - tw;
41606         this.el.setWidth( this.adjustWidth('input', x));
41607             
41608         this.trigger.setStyle('left', x+'px');
41609         
41610         if(this.list && this.listWidth === undefined){
41611             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41612             this.list.setWidth(lw);
41613             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41614         }
41615         
41616     
41617         
41618     },
41619
41620     /**
41621      * Allow or prevent the user from directly editing the field text.  If false is passed,
41622      * the user will only be able to select from the items defined in the dropdown list.  This method
41623      * is the runtime equivalent of setting the 'editable' config option at config time.
41624      * @param {Boolean} value True to allow the user to directly edit the field text
41625      */
41626     setEditable : function(value){
41627         if(value == this.editable){
41628             return;
41629         }
41630         this.editable = value;
41631         if(!value){
41632             this.el.dom.setAttribute('readOnly', true);
41633             this.el.on('mousedown', this.onTriggerClick,  this);
41634             this.el.addClass('x-combo-noedit');
41635         }else{
41636             this.el.dom.setAttribute('readOnly', false);
41637             this.el.un('mousedown', this.onTriggerClick,  this);
41638             this.el.removeClass('x-combo-noedit');
41639         }
41640     },
41641
41642     // private
41643     onBeforeLoad : function(){
41644         if(!this.hasFocus){
41645             return;
41646         }
41647         this.innerList.update(this.loadingText ?
41648                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41649         this.restrictHeight();
41650         this.selectedIndex = -1;
41651     },
41652
41653     // private
41654     onLoad : function(){
41655         if(!this.hasFocus){
41656             return;
41657         }
41658         if(this.store.getCount() > 0){
41659             this.expand();
41660             this.restrictHeight();
41661             if(this.lastQuery == this.allQuery){
41662                 if(this.editable){
41663                     this.el.dom.select();
41664                 }
41665                 if(!this.selectByValue(this.value, true)){
41666                     this.select(0, true);
41667                 }
41668             }else{
41669                 this.selectNext();
41670                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41671                     this.taTask.delay(this.typeAheadDelay);
41672                 }
41673             }
41674         }else{
41675             this.onEmptyResults();
41676         }
41677         //this.el.focus();
41678     },
41679     // private
41680     onLoadException : function()
41681     {
41682         this.collapse();
41683         Roo.log(this.store.reader.jsonData);
41684         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41685             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41686         }
41687         
41688         
41689     },
41690     // private
41691     onTypeAhead : function(){
41692         if(this.store.getCount() > 0){
41693             var r = this.store.getAt(0);
41694             var newValue = r.data[this.displayField];
41695             var len = newValue.length;
41696             var selStart = this.getRawValue().length;
41697             if(selStart != len){
41698                 this.setRawValue(newValue);
41699                 this.selectText(selStart, newValue.length);
41700             }
41701         }
41702     },
41703
41704     // private
41705     onSelect : function(record, index){
41706         if(this.fireEvent('beforeselect', this, record, index) !== false){
41707             this.setFromData(index > -1 ? record.data : false);
41708             this.collapse();
41709             this.fireEvent('select', this, record, index);
41710         }
41711     },
41712
41713     /**
41714      * Returns the currently selected field value or empty string if no value is set.
41715      * @return {String} value The selected value
41716      */
41717     getValue : function(){
41718         if(this.valueField){
41719             return typeof this.value != 'undefined' ? this.value : '';
41720         }
41721         return Roo.form.ComboBox.superclass.getValue.call(this);
41722     },
41723
41724     /**
41725      * Clears any text/value currently set in the field
41726      */
41727     clearValue : function(){
41728         if(this.hiddenField){
41729             this.hiddenField.value = '';
41730         }
41731         this.value = '';
41732         this.setRawValue('');
41733         this.lastSelectionText = '';
41734         
41735     },
41736
41737     /**
41738      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41739      * will be displayed in the field.  If the value does not match the data value of an existing item,
41740      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41741      * Otherwise the field will be blank (although the value will still be set).
41742      * @param {String} value The value to match
41743      */
41744     setValue : function(v){
41745         var text = v;
41746         if(this.valueField){
41747             var r = this.findRecord(this.valueField, v);
41748             if(r){
41749                 text = r.data[this.displayField];
41750             }else if(this.valueNotFoundText !== undefined){
41751                 text = this.valueNotFoundText;
41752             }
41753         }
41754         this.lastSelectionText = text;
41755         if(this.hiddenField){
41756             this.hiddenField.value = v;
41757         }
41758         Roo.form.ComboBox.superclass.setValue.call(this, text);
41759         this.value = v;
41760     },
41761     /**
41762      * @property {Object} the last set data for the element
41763      */
41764     
41765     lastData : false,
41766     /**
41767      * Sets the value of the field based on a object which is related to the record format for the store.
41768      * @param {Object} value the value to set as. or false on reset?
41769      */
41770     setFromData : function(o){
41771         var dv = ''; // display value
41772         var vv = ''; // value value..
41773         this.lastData = o;
41774         if (this.displayField) {
41775             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41776         } else {
41777             // this is an error condition!!!
41778             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41779         }
41780         
41781         if(this.valueField){
41782             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41783         }
41784         if(this.hiddenField){
41785             this.hiddenField.value = vv;
41786             
41787             this.lastSelectionText = dv;
41788             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41789             this.value = vv;
41790             return;
41791         }
41792         // no hidden field.. - we store the value in 'value', but still display
41793         // display field!!!!
41794         this.lastSelectionText = dv;
41795         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41796         this.value = vv;
41797         
41798         
41799     },
41800     // private
41801     reset : function(){
41802         // overridden so that last data is reset..
41803         this.setValue(this.resetValue);
41804         this.originalValue = this.getValue();
41805         this.clearInvalid();
41806         this.lastData = false;
41807         if (this.view) {
41808             this.view.clearSelections();
41809         }
41810     },
41811     // private
41812     findRecord : function(prop, value){
41813         var record;
41814         if(this.store.getCount() > 0){
41815             this.store.each(function(r){
41816                 if(r.data[prop] == value){
41817                     record = r;
41818                     return false;
41819                 }
41820                 return true;
41821             });
41822         }
41823         return record;
41824     },
41825     
41826     getName: function()
41827     {
41828         // returns hidden if it's set..
41829         if (!this.rendered) {return ''};
41830         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41831         
41832     },
41833     // private
41834     onViewMove : function(e, t){
41835         this.inKeyMode = false;
41836     },
41837
41838     // private
41839     onViewOver : function(e, t){
41840         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41841             return;
41842         }
41843         var item = this.view.findItemFromChild(t);
41844         if(item){
41845             var index = this.view.indexOf(item);
41846             this.select(index, false);
41847         }
41848     },
41849
41850     // private
41851     onViewClick : function(doFocus)
41852     {
41853         var index = this.view.getSelectedIndexes()[0];
41854         var r = this.store.getAt(index);
41855         if(r){
41856             this.onSelect(r, index);
41857         }
41858         if(doFocus !== false && !this.blockFocus){
41859             this.el.focus();
41860         }
41861     },
41862
41863     // private
41864     restrictHeight : function(){
41865         this.innerList.dom.style.height = '';
41866         var inner = this.innerList.dom;
41867         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41868         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41869         this.list.beginUpdate();
41870         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41871         this.list.alignTo(this.el, this.listAlign);
41872         this.list.endUpdate();
41873     },
41874
41875     // private
41876     onEmptyResults : function(){
41877         this.collapse();
41878     },
41879
41880     /**
41881      * Returns true if the dropdown list is expanded, else false.
41882      */
41883     isExpanded : function(){
41884         return this.list.isVisible();
41885     },
41886
41887     /**
41888      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41890      * @param {String} value The data value of the item to select
41891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41892      * selected item if it is not currently in view (defaults to true)
41893      * @return {Boolean} True if the value matched an item in the list, else false
41894      */
41895     selectByValue : function(v, scrollIntoView){
41896         if(v !== undefined && v !== null){
41897             var r = this.findRecord(this.valueField || this.displayField, v);
41898             if(r){
41899                 this.select(this.store.indexOf(r), scrollIntoView);
41900                 return true;
41901             }
41902         }
41903         return false;
41904     },
41905
41906     /**
41907      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41908      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41909      * @param {Number} index The zero-based index of the list item to select
41910      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41911      * selected item if it is not currently in view (defaults to true)
41912      */
41913     select : function(index, scrollIntoView){
41914         this.selectedIndex = index;
41915         this.view.select(index);
41916         if(scrollIntoView !== false){
41917             var el = this.view.getNode(index);
41918             if(el){
41919                 this.innerList.scrollChildIntoView(el, false);
41920             }
41921         }
41922     },
41923
41924     // private
41925     selectNext : function(){
41926         var ct = this.store.getCount();
41927         if(ct > 0){
41928             if(this.selectedIndex == -1){
41929                 this.select(0);
41930             }else if(this.selectedIndex < ct-1){
41931                 this.select(this.selectedIndex+1);
41932             }
41933         }
41934     },
41935
41936     // private
41937     selectPrev : function(){
41938         var ct = this.store.getCount();
41939         if(ct > 0){
41940             if(this.selectedIndex == -1){
41941                 this.select(0);
41942             }else if(this.selectedIndex != 0){
41943                 this.select(this.selectedIndex-1);
41944             }
41945         }
41946     },
41947
41948     // private
41949     onKeyUp : function(e){
41950         if(this.editable !== false && !e.isSpecialKey()){
41951             this.lastKey = e.getKey();
41952             this.dqTask.delay(this.queryDelay);
41953         }
41954     },
41955
41956     // private
41957     validateBlur : function(){
41958         return !this.list || !this.list.isVisible();   
41959     },
41960
41961     // private
41962     initQuery : function(){
41963         this.doQuery(this.getRawValue());
41964     },
41965
41966     // private
41967     doForce : function(){
41968         if(this.el.dom.value.length > 0){
41969             this.el.dom.value =
41970                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41971              
41972         }
41973     },
41974
41975     /**
41976      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41977      * query allowing the query action to be canceled if needed.
41978      * @param {String} query The SQL query to execute
41979      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41980      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41981      * saved in the current store (defaults to false)
41982      */
41983     doQuery : function(q, forceAll){
41984         if(q === undefined || q === null){
41985             q = '';
41986         }
41987         var qe = {
41988             query: q,
41989             forceAll: forceAll,
41990             combo: this,
41991             cancel:false
41992         };
41993         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41994             return false;
41995         }
41996         q = qe.query;
41997         forceAll = qe.forceAll;
41998         if(forceAll === true || (q.length >= this.minChars)){
41999             if(this.lastQuery != q || this.alwaysQuery){
42000                 this.lastQuery = q;
42001                 if(this.mode == 'local'){
42002                     this.selectedIndex = -1;
42003                     if(forceAll){
42004                         this.store.clearFilter();
42005                     }else{
42006                         this.store.filter(this.displayField, q);
42007                     }
42008                     this.onLoad();
42009                 }else{
42010                     this.store.baseParams[this.queryParam] = q;
42011                     this.store.load({
42012                         params: this.getParams(q)
42013                     });
42014                     this.expand();
42015                 }
42016             }else{
42017                 this.selectedIndex = -1;
42018                 this.onLoad();   
42019             }
42020         }
42021     },
42022
42023     // private
42024     getParams : function(q){
42025         var p = {};
42026         //p[this.queryParam] = q;
42027         if(this.pageSize){
42028             p.start = 0;
42029             p.limit = this.pageSize;
42030         }
42031         return p;
42032     },
42033
42034     /**
42035      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42036      */
42037     collapse : function(){
42038         if(!this.isExpanded()){
42039             return;
42040         }
42041         this.list.hide();
42042         Roo.get(document).un('mousedown', this.collapseIf, this);
42043         Roo.get(document).un('mousewheel', this.collapseIf, this);
42044         if (!this.editable) {
42045             Roo.get(document).un('keydown', this.listKeyPress, this);
42046         }
42047         this.fireEvent('collapse', this);
42048     },
42049
42050     // private
42051     collapseIf : function(e){
42052         if(!e.within(this.wrap) && !e.within(this.list)){
42053             this.collapse();
42054         }
42055     },
42056
42057     /**
42058      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42059      */
42060     expand : function(){
42061         if(this.isExpanded() || !this.hasFocus){
42062             return;
42063         }
42064         this.list.alignTo(this.el, this.listAlign);
42065         this.list.show();
42066         Roo.get(document).on('mousedown', this.collapseIf, this);
42067         Roo.get(document).on('mousewheel', this.collapseIf, this);
42068         if (!this.editable) {
42069             Roo.get(document).on('keydown', this.listKeyPress, this);
42070         }
42071         
42072         this.fireEvent('expand', this);
42073     },
42074
42075     // private
42076     // Implements the default empty TriggerField.onTriggerClick function
42077     onTriggerClick : function(){
42078         if(this.disabled){
42079             return;
42080         }
42081         if(this.isExpanded()){
42082             this.collapse();
42083             if (!this.blockFocus) {
42084                 this.el.focus();
42085             }
42086             
42087         }else {
42088             this.hasFocus = true;
42089             if(this.triggerAction == 'all') {
42090                 this.doQuery(this.allQuery, true);
42091             } else {
42092                 this.doQuery(this.getRawValue());
42093             }
42094             if (!this.blockFocus) {
42095                 this.el.focus();
42096             }
42097         }
42098     },
42099     listKeyPress : function(e)
42100     {
42101         //Roo.log('listkeypress');
42102         // scroll to first matching element based on key pres..
42103         if (e.isSpecialKey()) {
42104             return false;
42105         }
42106         var k = String.fromCharCode(e.getKey()).toUpperCase();
42107         //Roo.log(k);
42108         var match  = false;
42109         var csel = this.view.getSelectedNodes();
42110         var cselitem = false;
42111         if (csel.length) {
42112             var ix = this.view.indexOf(csel[0]);
42113             cselitem  = this.store.getAt(ix);
42114             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42115                 cselitem = false;
42116             }
42117             
42118         }
42119         
42120         this.store.each(function(v) { 
42121             if (cselitem) {
42122                 // start at existing selection.
42123                 if (cselitem.id == v.id) {
42124                     cselitem = false;
42125                 }
42126                 return;
42127             }
42128                 
42129             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42130                 match = this.store.indexOf(v);
42131                 return false;
42132             }
42133         }, this);
42134         
42135         if (match === false) {
42136             return true; // no more action?
42137         }
42138         // scroll to?
42139         this.view.select(match);
42140         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42141         sn.scrollIntoView(sn.dom.parentNode, false);
42142     } 
42143
42144     /** 
42145     * @cfg {Boolean} grow 
42146     * @hide 
42147     */
42148     /** 
42149     * @cfg {Number} growMin 
42150     * @hide 
42151     */
42152     /** 
42153     * @cfg {Number} growMax 
42154     * @hide 
42155     */
42156     /**
42157      * @hide
42158      * @method autoSize
42159      */
42160 });/*
42161  * Copyright(c) 2010-2012, Roo J Solutions Limited
42162  *
42163  * Licence LGPL
42164  *
42165  */
42166
42167 /**
42168  * @class Roo.form.ComboBoxArray
42169  * @extends Roo.form.TextField
42170  * A facebook style adder... for lists of email / people / countries  etc...
42171  * pick multiple items from a combo box, and shows each one.
42172  *
42173  *  Fred [x]  Brian [x]  [Pick another |v]
42174  *
42175  *
42176  *  For this to work: it needs various extra information
42177  *    - normal combo problay has
42178  *      name, hiddenName
42179  *    + displayField, valueField
42180  *
42181  *    For our purpose...
42182  *
42183  *
42184  *   If we change from 'extends' to wrapping...
42185  *   
42186  *  
42187  *
42188  
42189  
42190  * @constructor
42191  * Create a new ComboBoxArray.
42192  * @param {Object} config Configuration options
42193  */
42194  
42195
42196 Roo.form.ComboBoxArray = function(config)
42197 {
42198     this.addEvents({
42199         /**
42200          * @event beforeremove
42201          * Fires before remove the value from the list
42202              * @param {Roo.form.ComboBoxArray} _self This combo box array
42203              * @param {Roo.form.ComboBoxArray.Item} item removed item
42204              */
42205         'beforeremove' : true,
42206         /**
42207          * @event remove
42208          * Fires when remove the value from the list
42209              * @param {Roo.form.ComboBoxArray} _self This combo box array
42210              * @param {Roo.form.ComboBoxArray.Item} item removed item
42211              */
42212         'remove' : true
42213         
42214         
42215     });
42216     
42217     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42218     
42219     this.items = new Roo.util.MixedCollection(false);
42220     
42221     // construct the child combo...
42222     
42223     
42224     
42225     
42226    
42227     
42228 }
42229
42230  
42231 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42232
42233     /**
42234      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42235      */
42236     
42237     lastData : false,
42238     
42239     // behavies liek a hiddne field
42240     inputType:      'hidden',
42241     /**
42242      * @cfg {Number} width The width of the box that displays the selected element
42243      */ 
42244     width:          300,
42245
42246     
42247     
42248     /**
42249      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42250      */
42251     name : false,
42252     /**
42253      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42254      */
42255     hiddenName : false,
42256     
42257     
42258     // private the array of items that are displayed..
42259     items  : false,
42260     // private - the hidden field el.
42261     hiddenEl : false,
42262     // private - the filed el..
42263     el : false,
42264     
42265     //validateValue : function() { return true; }, // all values are ok!
42266     //onAddClick: function() { },
42267     
42268     onRender : function(ct, position) 
42269     {
42270         
42271         // create the standard hidden element
42272         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42273         
42274         
42275         // give fake names to child combo;
42276         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42277         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42278         
42279         this.combo = Roo.factory(this.combo, Roo.form);
42280         this.combo.onRender(ct, position);
42281         if (typeof(this.combo.width) != 'undefined') {
42282             this.combo.onResize(this.combo.width,0);
42283         }
42284         
42285         this.combo.initEvents();
42286         
42287         // assigned so form know we need to do this..
42288         this.store          = this.combo.store;
42289         this.valueField     = this.combo.valueField;
42290         this.displayField   = this.combo.displayField ;
42291         
42292         
42293         this.combo.wrap.addClass('x-cbarray-grp');
42294         
42295         var cbwrap = this.combo.wrap.createChild(
42296             {tag: 'div', cls: 'x-cbarray-cb'},
42297             this.combo.el.dom
42298         );
42299         
42300              
42301         this.hiddenEl = this.combo.wrap.createChild({
42302             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42303         });
42304         this.el = this.combo.wrap.createChild({
42305             tag: 'input',  type:'hidden' , name: this.name, value : ''
42306         });
42307          //   this.el.dom.removeAttribute("name");
42308         
42309         
42310         this.outerWrap = this.combo.wrap;
42311         this.wrap = cbwrap;
42312         
42313         this.outerWrap.setWidth(this.width);
42314         this.outerWrap.dom.removeChild(this.el.dom);
42315         
42316         this.wrap.dom.appendChild(this.el.dom);
42317         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42318         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42319         
42320         this.combo.trigger.setStyle('position','relative');
42321         this.combo.trigger.setStyle('left', '0px');
42322         this.combo.trigger.setStyle('top', '2px');
42323         
42324         this.combo.el.setStyle('vertical-align', 'text-bottom');
42325         
42326         //this.trigger.setStyle('vertical-align', 'top');
42327         
42328         // this should use the code from combo really... on('add' ....)
42329         if (this.adder) {
42330             
42331         
42332             this.adder = this.outerWrap.createChild(
42333                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42334             var _t = this;
42335             this.adder.on('click', function(e) {
42336                 _t.fireEvent('adderclick', this, e);
42337             }, _t);
42338         }
42339         //var _t = this;
42340         //this.adder.on('click', this.onAddClick, _t);
42341         
42342         
42343         this.combo.on('select', function(cb, rec, ix) {
42344             this.addItem(rec.data);
42345             
42346             cb.setValue('');
42347             cb.el.dom.value = '';
42348             //cb.lastData = rec.data;
42349             // add to list
42350             
42351         }, this);
42352         
42353         
42354     },
42355     
42356     
42357     getName: function()
42358     {
42359         // returns hidden if it's set..
42360         if (!this.rendered) {return ''};
42361         return  this.hiddenName ? this.hiddenName : this.name;
42362         
42363     },
42364     
42365     
42366     onResize: function(w, h){
42367         
42368         return;
42369         // not sure if this is needed..
42370         //this.combo.onResize(w,h);
42371         
42372         if(typeof w != 'number'){
42373             // we do not handle it!?!?
42374             return;
42375         }
42376         var tw = this.combo.trigger.getWidth();
42377         tw += this.addicon ? this.addicon.getWidth() : 0;
42378         tw += this.editicon ? this.editicon.getWidth() : 0;
42379         var x = w - tw;
42380         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42381             
42382         this.combo.trigger.setStyle('left', '0px');
42383         
42384         if(this.list && this.listWidth === undefined){
42385             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42386             this.list.setWidth(lw);
42387             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42388         }
42389         
42390     
42391         
42392     },
42393     
42394     addItem: function(rec)
42395     {
42396         var valueField = this.combo.valueField;
42397         var displayField = this.combo.displayField;
42398         
42399         if (this.items.indexOfKey(rec[valueField]) > -1) {
42400             //console.log("GOT " + rec.data.id);
42401             return;
42402         }
42403         
42404         var x = new Roo.form.ComboBoxArray.Item({
42405             //id : rec[this.idField],
42406             data : rec,
42407             displayField : displayField ,
42408             tipField : displayField ,
42409             cb : this
42410         });
42411         // use the 
42412         this.items.add(rec[valueField],x);
42413         // add it before the element..
42414         this.updateHiddenEl();
42415         x.render(this.outerWrap, this.wrap.dom);
42416         // add the image handler..
42417     },
42418     
42419     updateHiddenEl : function()
42420     {
42421         this.validate();
42422         if (!this.hiddenEl) {
42423             return;
42424         }
42425         var ar = [];
42426         var idField = this.combo.valueField;
42427         
42428         this.items.each(function(f) {
42429             ar.push(f.data[idField]);
42430         });
42431         this.hiddenEl.dom.value = ar.join(',');
42432         this.validate();
42433     },
42434     
42435     reset : function()
42436     {
42437         this.items.clear();
42438         
42439         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42440            el.remove();
42441         });
42442         
42443         this.el.dom.value = '';
42444         if (this.hiddenEl) {
42445             this.hiddenEl.dom.value = '';
42446         }
42447         
42448     },
42449     getValue: function()
42450     {
42451         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42452     },
42453     setValue: function(v) // not a valid action - must use addItems..
42454     {
42455         
42456         this.reset();
42457          
42458         if (this.store.isLocal && (typeof(v) == 'string')) {
42459             // then we can use the store to find the values..
42460             // comma seperated at present.. this needs to allow JSON based encoding..
42461             this.hiddenEl.value  = v;
42462             var v_ar = [];
42463             Roo.each(v.split(','), function(k) {
42464                 Roo.log("CHECK " + this.valueField + ',' + k);
42465                 var li = this.store.query(this.valueField, k);
42466                 if (!li.length) {
42467                     return;
42468                 }
42469                 var add = {};
42470                 add[this.valueField] = k;
42471                 add[this.displayField] = li.item(0).data[this.displayField];
42472                 
42473                 this.addItem(add);
42474             }, this) 
42475              
42476         }
42477         if (typeof(v) == 'object' ) {
42478             // then let's assume it's an array of objects..
42479             Roo.each(v, function(l) {
42480                 this.addItem(l);
42481             }, this);
42482              
42483         }
42484         
42485         
42486     },
42487     setFromData: function(v)
42488     {
42489         // this recieves an object, if setValues is called.
42490         this.reset();
42491         this.el.dom.value = v[this.displayField];
42492         this.hiddenEl.dom.value = v[this.valueField];
42493         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42494             return;
42495         }
42496         var kv = v[this.valueField];
42497         var dv = v[this.displayField];
42498         kv = typeof(kv) != 'string' ? '' : kv;
42499         dv = typeof(dv) != 'string' ? '' : dv;
42500         
42501         
42502         var keys = kv.split(',');
42503         var display = dv.split(',');
42504         for (var i = 0 ; i < keys.length; i++) {
42505             
42506             add = {};
42507             add[this.valueField] = keys[i];
42508             add[this.displayField] = display[i];
42509             this.addItem(add);
42510         }
42511       
42512         
42513     },
42514     
42515     /**
42516      * Validates the combox array value
42517      * @return {Boolean} True if the value is valid, else false
42518      */
42519     validate : function(){
42520         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42521             this.clearInvalid();
42522             return true;
42523         }
42524         return false;
42525     },
42526     
42527     validateValue : function(value){
42528         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42529         
42530     },
42531     
42532     /*@
42533      * overide
42534      * 
42535      */
42536     isDirty : function() {
42537         if(this.disabled) {
42538             return false;
42539         }
42540         
42541         try {
42542             var d = Roo.decode(String(this.originalValue));
42543         } catch (e) {
42544             return String(this.getValue()) !== String(this.originalValue);
42545         }
42546         
42547         var originalValue = [];
42548         
42549         for (var i = 0; i < d.length; i++){
42550             originalValue.push(d[i][this.valueField]);
42551         }
42552         
42553         return String(this.getValue()) !== String(originalValue.join(','));
42554         
42555     }
42556     
42557 });
42558
42559
42560
42561 /**
42562  * @class Roo.form.ComboBoxArray.Item
42563  * @extends Roo.BoxComponent
42564  * A selected item in the list
42565  *  Fred [x]  Brian [x]  [Pick another |v]
42566  * 
42567  * @constructor
42568  * Create a new item.
42569  * @param {Object} config Configuration options
42570  */
42571  
42572 Roo.form.ComboBoxArray.Item = function(config) {
42573     config.id = Roo.id();
42574     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42575 }
42576
42577 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42578     data : {},
42579     cb: false,
42580     displayField : false,
42581     tipField : false,
42582     
42583     
42584     defaultAutoCreate : {
42585         tag: 'div',
42586         cls: 'x-cbarray-item',
42587         cn : [ 
42588             { tag: 'div' },
42589             {
42590                 tag: 'img',
42591                 width:16,
42592                 height : 16,
42593                 src : Roo.BLANK_IMAGE_URL ,
42594                 align: 'center'
42595             }
42596         ]
42597         
42598     },
42599     
42600  
42601     onRender : function(ct, position)
42602     {
42603         Roo.form.Field.superclass.onRender.call(this, ct, position);
42604         
42605         if(!this.el){
42606             var cfg = this.getAutoCreate();
42607             this.el = ct.createChild(cfg, position);
42608         }
42609         
42610         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42611         
42612         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42613             this.cb.renderer(this.data) :
42614             String.format('{0}',this.data[this.displayField]);
42615         
42616             
42617         this.el.child('div').dom.setAttribute('qtip',
42618                         String.format('{0}',this.data[this.tipField])
42619         );
42620         
42621         this.el.child('img').on('click', this.remove, this);
42622         
42623     },
42624    
42625     remove : function()
42626     {
42627         if(this.cb.disabled){
42628             return;
42629         }
42630         
42631         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42632             this.cb.items.remove(this);
42633             this.el.child('img').un('click', this.remove, this);
42634             this.el.remove();
42635             this.cb.updateHiddenEl();
42636
42637             this.cb.fireEvent('remove', this.cb, this);
42638         }
42639         
42640     }
42641 });/*
42642  * Based on:
42643  * Ext JS Library 1.1.1
42644  * Copyright(c) 2006-2007, Ext JS, LLC.
42645  *
42646  * Originally Released Under LGPL - original licence link has changed is not relivant.
42647  *
42648  * Fork - LGPL
42649  * <script type="text/javascript">
42650  */
42651 /**
42652  * @class Roo.form.Checkbox
42653  * @extends Roo.form.Field
42654  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42655  * @constructor
42656  * Creates a new Checkbox
42657  * @param {Object} config Configuration options
42658  */
42659 Roo.form.Checkbox = function(config){
42660     Roo.form.Checkbox.superclass.constructor.call(this, config);
42661     this.addEvents({
42662         /**
42663          * @event check
42664          * Fires when the checkbox is checked or unchecked.
42665              * @param {Roo.form.Checkbox} this This checkbox
42666              * @param {Boolean} checked The new checked value
42667              */
42668         check : true
42669     });
42670 };
42671
42672 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42673     /**
42674      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42675      */
42676     focusClass : undefined,
42677     /**
42678      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42679      */
42680     fieldClass: "x-form-field",
42681     /**
42682      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42683      */
42684     checked: false,
42685     /**
42686      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42687      * {tag: "input", type: "checkbox", autocomplete: "off"})
42688      */
42689     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42690     /**
42691      * @cfg {String} boxLabel The text that appears beside the checkbox
42692      */
42693     boxLabel : "",
42694     /**
42695      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42696      */  
42697     inputValue : '1',
42698     /**
42699      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42700      */
42701      valueOff: '0', // value when not checked..
42702
42703     actionMode : 'viewEl', 
42704     //
42705     // private
42706     itemCls : 'x-menu-check-item x-form-item',
42707     groupClass : 'x-menu-group-item',
42708     inputType : 'hidden',
42709     
42710     
42711     inSetChecked: false, // check that we are not calling self...
42712     
42713     inputElement: false, // real input element?
42714     basedOn: false, // ????
42715     
42716     isFormField: true, // not sure where this is needed!!!!
42717
42718     onResize : function(){
42719         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42720         if(!this.boxLabel){
42721             this.el.alignTo(this.wrap, 'c-c');
42722         }
42723     },
42724
42725     initEvents : function(){
42726         Roo.form.Checkbox.superclass.initEvents.call(this);
42727         this.el.on("click", this.onClick,  this);
42728         this.el.on("change", this.onClick,  this);
42729     },
42730
42731
42732     getResizeEl : function(){
42733         return this.wrap;
42734     },
42735
42736     getPositionEl : function(){
42737         return this.wrap;
42738     },
42739
42740     // private
42741     onRender : function(ct, position){
42742         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42743         /*
42744         if(this.inputValue !== undefined){
42745             this.el.dom.value = this.inputValue;
42746         }
42747         */
42748         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42749         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42750         var viewEl = this.wrap.createChild({ 
42751             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42752         this.viewEl = viewEl;   
42753         this.wrap.on('click', this.onClick,  this); 
42754         
42755         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42756         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42757         
42758         
42759         
42760         if(this.boxLabel){
42761             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42762         //    viewEl.on('click', this.onClick,  this); 
42763         }
42764         //if(this.checked){
42765             this.setChecked(this.checked);
42766         //}else{
42767             //this.checked = this.el.dom;
42768         //}
42769
42770     },
42771
42772     // private
42773     initValue : Roo.emptyFn,
42774
42775     /**
42776      * Returns the checked state of the checkbox.
42777      * @return {Boolean} True if checked, else false
42778      */
42779     getValue : function(){
42780         if(this.el){
42781             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42782         }
42783         return this.valueOff;
42784         
42785     },
42786
42787         // private
42788     onClick : function(){ 
42789         if (this.disabled) {
42790             return;
42791         }
42792         this.setChecked(!this.checked);
42793
42794         //if(this.el.dom.checked != this.checked){
42795         //    this.setValue(this.el.dom.checked);
42796        // }
42797     },
42798
42799     /**
42800      * Sets the checked state of the checkbox.
42801      * On is always based on a string comparison between inputValue and the param.
42802      * @param {Boolean/String} value - the value to set 
42803      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42804      */
42805     setValue : function(v,suppressEvent){
42806         
42807         
42808         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42809         //if(this.el && this.el.dom){
42810         //    this.el.dom.checked = this.checked;
42811         //    this.el.dom.defaultChecked = this.checked;
42812         //}
42813         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42814         //this.fireEvent("check", this, this.checked);
42815     },
42816     // private..
42817     setChecked : function(state,suppressEvent)
42818     {
42819         if (this.inSetChecked) {
42820             this.checked = state;
42821             return;
42822         }
42823         
42824     
42825         if(this.wrap){
42826             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42827         }
42828         this.checked = state;
42829         if(suppressEvent !== true){
42830             this.fireEvent('check', this, state);
42831         }
42832         this.inSetChecked = true;
42833         this.el.dom.value = state ? this.inputValue : this.valueOff;
42834         this.inSetChecked = false;
42835         
42836     },
42837     // handle setting of hidden value by some other method!!?!?
42838     setFromHidden: function()
42839     {
42840         if(!this.el){
42841             return;
42842         }
42843         //console.log("SET FROM HIDDEN");
42844         //alert('setFrom hidden');
42845         this.setValue(this.el.dom.value);
42846     },
42847     
42848     onDestroy : function()
42849     {
42850         if(this.viewEl){
42851             Roo.get(this.viewEl).remove();
42852         }
42853          
42854         Roo.form.Checkbox.superclass.onDestroy.call(this);
42855     },
42856     
42857     setBoxLabel : function(str)
42858     {
42859         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42860     }
42861
42862 });/*
42863  * Based on:
42864  * Ext JS Library 1.1.1
42865  * Copyright(c) 2006-2007, Ext JS, LLC.
42866  *
42867  * Originally Released Under LGPL - original licence link has changed is not relivant.
42868  *
42869  * Fork - LGPL
42870  * <script type="text/javascript">
42871  */
42872  
42873 /**
42874  * @class Roo.form.Radio
42875  * @extends Roo.form.Checkbox
42876  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42877  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42878  * @constructor
42879  * Creates a new Radio
42880  * @param {Object} config Configuration options
42881  */
42882 Roo.form.Radio = function(){
42883     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42884 };
42885 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42886     inputType: 'radio',
42887
42888     /**
42889      * If this radio is part of a group, it will return the selected value
42890      * @return {String}
42891      */
42892     getGroupValue : function(){
42893         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42894     },
42895     
42896     
42897     onRender : function(ct, position){
42898         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42899         
42900         if(this.inputValue !== undefined){
42901             this.el.dom.value = this.inputValue;
42902         }
42903          
42904         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42905         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42906         //var viewEl = this.wrap.createChild({ 
42907         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42908         //this.viewEl = viewEl;   
42909         //this.wrap.on('click', this.onClick,  this); 
42910         
42911         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42912         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42913         
42914         
42915         
42916         if(this.boxLabel){
42917             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42918         //    viewEl.on('click', this.onClick,  this); 
42919         }
42920          if(this.checked){
42921             this.el.dom.checked =   'checked' ;
42922         }
42923          
42924     } 
42925     
42926     
42927 });//<script type="text/javascript">
42928
42929 /*
42930  * Based  Ext JS Library 1.1.1
42931  * Copyright(c) 2006-2007, Ext JS, LLC.
42932  * LGPL
42933  *
42934  */
42935  
42936 /**
42937  * @class Roo.HtmlEditorCore
42938  * @extends Roo.Component
42939  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42940  *
42941  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42942  */
42943
42944 Roo.HtmlEditorCore = function(config){
42945     
42946     
42947     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42948     
42949     
42950     this.addEvents({
42951         /**
42952          * @event initialize
42953          * Fires when the editor is fully initialized (including the iframe)
42954          * @param {Roo.HtmlEditorCore} this
42955          */
42956         initialize: true,
42957         /**
42958          * @event activate
42959          * Fires when the editor is first receives the focus. Any insertion must wait
42960          * until after this event.
42961          * @param {Roo.HtmlEditorCore} this
42962          */
42963         activate: true,
42964          /**
42965          * @event beforesync
42966          * Fires before the textarea is updated with content from the editor iframe. Return false
42967          * to cancel the sync.
42968          * @param {Roo.HtmlEditorCore} this
42969          * @param {String} html
42970          */
42971         beforesync: true,
42972          /**
42973          * @event beforepush
42974          * Fires before the iframe editor is updated with content from the textarea. Return false
42975          * to cancel the push.
42976          * @param {Roo.HtmlEditorCore} this
42977          * @param {String} html
42978          */
42979         beforepush: true,
42980          /**
42981          * @event sync
42982          * Fires when the textarea is updated with content from the editor iframe.
42983          * @param {Roo.HtmlEditorCore} this
42984          * @param {String} html
42985          */
42986         sync: true,
42987          /**
42988          * @event push
42989          * Fires when the iframe editor is updated with content from the textarea.
42990          * @param {Roo.HtmlEditorCore} this
42991          * @param {String} html
42992          */
42993         push: true,
42994         
42995         /**
42996          * @event editorevent
42997          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42998          * @param {Roo.HtmlEditorCore} this
42999          */
43000         editorevent: true
43001         
43002     });
43003     
43004     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43005     
43006     // defaults : white / black...
43007     this.applyBlacklists();
43008     
43009     
43010     
43011 };
43012
43013
43014 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43015
43016
43017      /**
43018      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43019      */
43020     
43021     owner : false,
43022     
43023      /**
43024      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43025      *                        Roo.resizable.
43026      */
43027     resizable : false,
43028      /**
43029      * @cfg {Number} height (in pixels)
43030      */   
43031     height: 300,
43032    /**
43033      * @cfg {Number} width (in pixels)
43034      */   
43035     width: 500,
43036     
43037     /**
43038      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43039      * 
43040      */
43041     stylesheets: false,
43042     
43043     // id of frame..
43044     frameId: false,
43045     
43046     // private properties
43047     validationEvent : false,
43048     deferHeight: true,
43049     initialized : false,
43050     activated : false,
43051     sourceEditMode : false,
43052     onFocus : Roo.emptyFn,
43053     iframePad:3,
43054     hideMode:'offsets',
43055     
43056     clearUp: true,
43057     
43058     // blacklist + whitelisted elements..
43059     black: false,
43060     white: false,
43061      
43062     bodyCls : '',
43063
43064     /**
43065      * Protected method that will not generally be called directly. It
43066      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43067      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43068      */
43069     getDocMarkup : function(){
43070         // body styles..
43071         var st = '';
43072         
43073         // inherit styels from page...?? 
43074         if (this.stylesheets === false) {
43075             
43076             Roo.get(document.head).select('style').each(function(node) {
43077                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43078             });
43079             
43080             Roo.get(document.head).select('link').each(function(node) { 
43081                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43082             });
43083             
43084         } else if (!this.stylesheets.length) {
43085                 // simple..
43086                 st = '<style type="text/css">' +
43087                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43088                    '</style>';
43089         } else { 
43090             st = '<style type="text/css">' +
43091                     this.stylesheets +
43092                 '</style>';
43093         }
43094         
43095         st +=  '<style type="text/css">' +
43096             'IMG { cursor: pointer } ' +
43097         '</style>';
43098
43099         var cls = 'roo-htmleditor-body';
43100         
43101         if(this.bodyCls.length){
43102             cls += ' ' + this.bodyCls;
43103         }
43104         
43105         return '<html><head>' + st  +
43106             //<style type="text/css">' +
43107             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43108             //'</style>' +
43109             ' </head><body class="' +  cls + '"></body></html>';
43110     },
43111
43112     // private
43113     onRender : function(ct, position)
43114     {
43115         var _t = this;
43116         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43117         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43118         
43119         
43120         this.el.dom.style.border = '0 none';
43121         this.el.dom.setAttribute('tabIndex', -1);
43122         this.el.addClass('x-hidden hide');
43123         
43124         
43125         
43126         if(Roo.isIE){ // fix IE 1px bogus margin
43127             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43128         }
43129        
43130         
43131         this.frameId = Roo.id();
43132         
43133          
43134         
43135         var iframe = this.owner.wrap.createChild({
43136             tag: 'iframe',
43137             cls: 'form-control', // bootstrap..
43138             id: this.frameId,
43139             name: this.frameId,
43140             frameBorder : 'no',
43141             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43142         }, this.el
43143         );
43144         
43145         
43146         this.iframe = iframe.dom;
43147
43148          this.assignDocWin();
43149         
43150         this.doc.designMode = 'on';
43151        
43152         this.doc.open();
43153         this.doc.write(this.getDocMarkup());
43154         this.doc.close();
43155
43156         
43157         var task = { // must defer to wait for browser to be ready
43158             run : function(){
43159                 //console.log("run task?" + this.doc.readyState);
43160                 this.assignDocWin();
43161                 if(this.doc.body || this.doc.readyState == 'complete'){
43162                     try {
43163                         this.doc.designMode="on";
43164                     } catch (e) {
43165                         return;
43166                     }
43167                     Roo.TaskMgr.stop(task);
43168                     this.initEditor.defer(10, this);
43169                 }
43170             },
43171             interval : 10,
43172             duration: 10000,
43173             scope: this
43174         };
43175         Roo.TaskMgr.start(task);
43176
43177     },
43178
43179     // private
43180     onResize : function(w, h)
43181     {
43182          Roo.log('resize: ' +w + ',' + h );
43183         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43184         if(!this.iframe){
43185             return;
43186         }
43187         if(typeof w == 'number'){
43188             
43189             this.iframe.style.width = w + 'px';
43190         }
43191         if(typeof h == 'number'){
43192             
43193             this.iframe.style.height = h + 'px';
43194             if(this.doc){
43195                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43196             }
43197         }
43198         
43199     },
43200
43201     /**
43202      * Toggles the editor between standard and source edit mode.
43203      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43204      */
43205     toggleSourceEdit : function(sourceEditMode){
43206         
43207         this.sourceEditMode = sourceEditMode === true;
43208         
43209         if(this.sourceEditMode){
43210  
43211             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43212             
43213         }else{
43214             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43215             //this.iframe.className = '';
43216             this.deferFocus();
43217         }
43218         //this.setSize(this.owner.wrap.getSize());
43219         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43220     },
43221
43222     
43223   
43224
43225     /**
43226      * Protected method that will not generally be called directly. If you need/want
43227      * custom HTML cleanup, this is the method you should override.
43228      * @param {String} html The HTML to be cleaned
43229      * return {String} The cleaned HTML
43230      */
43231     cleanHtml : function(html){
43232         html = String(html);
43233         if(html.length > 5){
43234             if(Roo.isSafari){ // strip safari nonsense
43235                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43236             }
43237         }
43238         if(html == '&nbsp;'){
43239             html = '';
43240         }
43241         return html;
43242     },
43243
43244     /**
43245      * HTML Editor -> Textarea
43246      * Protected method that will not generally be called directly. Syncs the contents
43247      * of the editor iframe with the textarea.
43248      */
43249     syncValue : function(){
43250         if(this.initialized){
43251             var bd = (this.doc.body || this.doc.documentElement);
43252             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43253             var html = bd.innerHTML;
43254             if(Roo.isSafari){
43255                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43256                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43257                 if(m && m[1]){
43258                     html = '<div style="'+m[0]+'">' + html + '</div>';
43259                 }
43260             }
43261             html = this.cleanHtml(html);
43262             // fix up the special chars.. normaly like back quotes in word...
43263             // however we do not want to do this with chinese..
43264             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43265                 
43266                 var cc = match.charCodeAt();
43267
43268                 // Get the character value, handling surrogate pairs
43269                 if (match.length == 2) {
43270                     // It's a surrogate pair, calculate the Unicode code point
43271                     var high = match.charCodeAt(0) - 0xD800;
43272                     var low  = match.charCodeAt(1) - 0xDC00;
43273                     cc = (high * 0x400) + low + 0x10000;
43274                 }  else if (
43275                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43276                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43277                     (cc >= 0xf900 && cc < 0xfb00 )
43278                 ) {
43279                         return match;
43280                 }  
43281          
43282                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43283                 return "&#" + cc + ";";
43284                 
43285                 
43286             });
43287             
43288             
43289              
43290             if(this.owner.fireEvent('beforesync', this, html) !== false){
43291                 this.el.dom.value = html;
43292                 this.owner.fireEvent('sync', this, html);
43293             }
43294         }
43295     },
43296
43297     /**
43298      * Protected method that will not generally be called directly. Pushes the value of the textarea
43299      * into the iframe editor.
43300      */
43301     pushValue : function(){
43302         if(this.initialized){
43303             var v = this.el.dom.value.trim();
43304             
43305 //            if(v.length < 1){
43306 //                v = '&#160;';
43307 //            }
43308             
43309             if(this.owner.fireEvent('beforepush', this, v) !== false){
43310                 var d = (this.doc.body || this.doc.documentElement);
43311                 d.innerHTML = v;
43312                 this.cleanUpPaste();
43313                 this.el.dom.value = d.innerHTML;
43314                 this.owner.fireEvent('push', this, v);
43315             }
43316         }
43317     },
43318
43319     // private
43320     deferFocus : function(){
43321         this.focus.defer(10, this);
43322     },
43323
43324     // doc'ed in Field
43325     focus : function(){
43326         if(this.win && !this.sourceEditMode){
43327             this.win.focus();
43328         }else{
43329             this.el.focus();
43330         }
43331     },
43332     
43333     assignDocWin: function()
43334     {
43335         var iframe = this.iframe;
43336         
43337          if(Roo.isIE){
43338             this.doc = iframe.contentWindow.document;
43339             this.win = iframe.contentWindow;
43340         } else {
43341 //            if (!Roo.get(this.frameId)) {
43342 //                return;
43343 //            }
43344 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43345 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43346             
43347             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43348                 return;
43349             }
43350             
43351             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43352             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43353         }
43354     },
43355     
43356     // private
43357     initEditor : function(){
43358         //console.log("INIT EDITOR");
43359         this.assignDocWin();
43360         
43361         
43362         
43363         this.doc.designMode="on";
43364         this.doc.open();
43365         this.doc.write(this.getDocMarkup());
43366         this.doc.close();
43367         
43368         var dbody = (this.doc.body || this.doc.documentElement);
43369         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43370         // this copies styles from the containing element into thsi one..
43371         // not sure why we need all of this..
43372         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43373         
43374         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43375         //ss['background-attachment'] = 'fixed'; // w3c
43376         dbody.bgProperties = 'fixed'; // ie
43377         //Roo.DomHelper.applyStyles(dbody, ss);
43378         Roo.EventManager.on(this.doc, {
43379             //'mousedown': this.onEditorEvent,
43380             'mouseup': this.onEditorEvent,
43381             'dblclick': this.onEditorEvent,
43382             'click': this.onEditorEvent,
43383             'keyup': this.onEditorEvent,
43384             buffer:100,
43385             scope: this
43386         });
43387         if(Roo.isGecko){
43388             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43389         }
43390         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43391             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43392         }
43393         this.initialized = true;
43394
43395         this.owner.fireEvent('initialize', this);
43396         this.pushValue();
43397     },
43398
43399     // private
43400     onDestroy : function(){
43401         
43402         
43403         
43404         if(this.rendered){
43405             
43406             //for (var i =0; i < this.toolbars.length;i++) {
43407             //    // fixme - ask toolbars for heights?
43408             //    this.toolbars[i].onDestroy();
43409            // }
43410             
43411             //this.wrap.dom.innerHTML = '';
43412             //this.wrap.remove();
43413         }
43414     },
43415
43416     // private
43417     onFirstFocus : function(){
43418         
43419         this.assignDocWin();
43420         
43421         
43422         this.activated = true;
43423          
43424     
43425         if(Roo.isGecko){ // prevent silly gecko errors
43426             this.win.focus();
43427             var s = this.win.getSelection();
43428             if(!s.focusNode || s.focusNode.nodeType != 3){
43429                 var r = s.getRangeAt(0);
43430                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43431                 r.collapse(true);
43432                 this.deferFocus();
43433             }
43434             try{
43435                 this.execCmd('useCSS', true);
43436                 this.execCmd('styleWithCSS', false);
43437             }catch(e){}
43438         }
43439         this.owner.fireEvent('activate', this);
43440     },
43441
43442     // private
43443     adjustFont: function(btn){
43444         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43445         //if(Roo.isSafari){ // safari
43446         //    adjust *= 2;
43447        // }
43448         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43449         if(Roo.isSafari){ // safari
43450             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43451             v =  (v < 10) ? 10 : v;
43452             v =  (v > 48) ? 48 : v;
43453             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43454             
43455         }
43456         
43457         
43458         v = Math.max(1, v+adjust);
43459         
43460         this.execCmd('FontSize', v  );
43461     },
43462
43463     onEditorEvent : function(e)
43464     {
43465         this.owner.fireEvent('editorevent', this, e);
43466       //  this.updateToolbar();
43467         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43468     },
43469
43470     insertTag : function(tg)
43471     {
43472         // could be a bit smarter... -> wrap the current selected tRoo..
43473         if (tg.toLowerCase() == 'span' ||
43474             tg.toLowerCase() == 'code' ||
43475             tg.toLowerCase() == 'sup' ||
43476             tg.toLowerCase() == 'sub' 
43477             ) {
43478             
43479             range = this.createRange(this.getSelection());
43480             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43481             wrappingNode.appendChild(range.extractContents());
43482             range.insertNode(wrappingNode);
43483
43484             return;
43485             
43486             
43487             
43488         }
43489         this.execCmd("formatblock",   tg);
43490         
43491     },
43492     
43493     insertText : function(txt)
43494     {
43495         
43496         
43497         var range = this.createRange();
43498         range.deleteContents();
43499                //alert(Sender.getAttribute('label'));
43500                
43501         range.insertNode(this.doc.createTextNode(txt));
43502     } ,
43503     
43504      
43505
43506     /**
43507      * Executes a Midas editor command on the editor document and performs necessary focus and
43508      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43509      * @param {String} cmd The Midas command
43510      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43511      */
43512     relayCmd : function(cmd, value){
43513         this.win.focus();
43514         this.execCmd(cmd, value);
43515         this.owner.fireEvent('editorevent', this);
43516         //this.updateToolbar();
43517         this.owner.deferFocus();
43518     },
43519
43520     /**
43521      * Executes a Midas editor command directly on the editor document.
43522      * For visual commands, you should use {@link #relayCmd} instead.
43523      * <b>This should only be called after the editor is initialized.</b>
43524      * @param {String} cmd The Midas command
43525      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43526      */
43527     execCmd : function(cmd, value){
43528         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43529         this.syncValue();
43530     },
43531  
43532  
43533    
43534     /**
43535      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43536      * to insert tRoo.
43537      * @param {String} text | dom node.. 
43538      */
43539     insertAtCursor : function(text)
43540     {
43541         
43542         if(!this.activated){
43543             return;
43544         }
43545         /*
43546         if(Roo.isIE){
43547             this.win.focus();
43548             var r = this.doc.selection.createRange();
43549             if(r){
43550                 r.collapse(true);
43551                 r.pasteHTML(text);
43552                 this.syncValue();
43553                 this.deferFocus();
43554             
43555             }
43556             return;
43557         }
43558         */
43559         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43560             this.win.focus();
43561             
43562             
43563             // from jquery ui (MIT licenced)
43564             var range, node;
43565             var win = this.win;
43566             
43567             if (win.getSelection && win.getSelection().getRangeAt) {
43568                 range = win.getSelection().getRangeAt(0);
43569                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43570                 range.insertNode(node);
43571             } else if (win.document.selection && win.document.selection.createRange) {
43572                 // no firefox support
43573                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43574                 win.document.selection.createRange().pasteHTML(txt);
43575             } else {
43576                 // no firefox support
43577                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43578                 this.execCmd('InsertHTML', txt);
43579             } 
43580             
43581             this.syncValue();
43582             
43583             this.deferFocus();
43584         }
43585     },
43586  // private
43587     mozKeyPress : function(e){
43588         if(e.ctrlKey){
43589             var c = e.getCharCode(), cmd;
43590           
43591             if(c > 0){
43592                 c = String.fromCharCode(c).toLowerCase();
43593                 switch(c){
43594                     case 'b':
43595                         cmd = 'bold';
43596                         break;
43597                     case 'i':
43598                         cmd = 'italic';
43599                         break;
43600                     
43601                     case 'u':
43602                         cmd = 'underline';
43603                         break;
43604                     
43605                     case 'v':
43606                         this.cleanUpPaste.defer(100, this);
43607                         return;
43608                         
43609                 }
43610                 if(cmd){
43611                     this.win.focus();
43612                     this.execCmd(cmd);
43613                     this.deferFocus();
43614                     e.preventDefault();
43615                 }
43616                 
43617             }
43618         }
43619     },
43620
43621     // private
43622     fixKeys : function(){ // load time branching for fastest keydown performance
43623         if(Roo.isIE){
43624             return function(e){
43625                 var k = e.getKey(), r;
43626                 if(k == e.TAB){
43627                     e.stopEvent();
43628                     r = this.doc.selection.createRange();
43629                     if(r){
43630                         r.collapse(true);
43631                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43632                         this.deferFocus();
43633                     }
43634                     return;
43635                 }
43636                 
43637                 if(k == e.ENTER){
43638                     r = this.doc.selection.createRange();
43639                     if(r){
43640                         var target = r.parentElement();
43641                         if(!target || target.tagName.toLowerCase() != 'li'){
43642                             e.stopEvent();
43643                             r.pasteHTML('<br />');
43644                             r.collapse(false);
43645                             r.select();
43646                         }
43647                     }
43648                 }
43649                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43650                     this.cleanUpPaste.defer(100, this);
43651                     return;
43652                 }
43653                 
43654                 
43655             };
43656         }else if(Roo.isOpera){
43657             return function(e){
43658                 var k = e.getKey();
43659                 if(k == e.TAB){
43660                     e.stopEvent();
43661                     this.win.focus();
43662                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43663                     this.deferFocus();
43664                 }
43665                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43666                     this.cleanUpPaste.defer(100, this);
43667                     return;
43668                 }
43669                 
43670             };
43671         }else if(Roo.isSafari){
43672             return function(e){
43673                 var k = e.getKey();
43674                 
43675                 if(k == e.TAB){
43676                     e.stopEvent();
43677                     this.execCmd('InsertText','\t');
43678                     this.deferFocus();
43679                     return;
43680                 }
43681                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43682                     this.cleanUpPaste.defer(100, this);
43683                     return;
43684                 }
43685                 
43686              };
43687         }
43688     }(),
43689     
43690     getAllAncestors: function()
43691     {
43692         var p = this.getSelectedNode();
43693         var a = [];
43694         if (!p) {
43695             a.push(p); // push blank onto stack..
43696             p = this.getParentElement();
43697         }
43698         
43699         
43700         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43701             a.push(p);
43702             p = p.parentNode;
43703         }
43704         a.push(this.doc.body);
43705         return a;
43706     },
43707     lastSel : false,
43708     lastSelNode : false,
43709     
43710     
43711     getSelection : function() 
43712     {
43713         this.assignDocWin();
43714         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43715     },
43716     
43717     getSelectedNode: function() 
43718     {
43719         // this may only work on Gecko!!!
43720         
43721         // should we cache this!!!!
43722         
43723         
43724         
43725          
43726         var range = this.createRange(this.getSelection()).cloneRange();
43727         
43728         if (Roo.isIE) {
43729             var parent = range.parentElement();
43730             while (true) {
43731                 var testRange = range.duplicate();
43732                 testRange.moveToElementText(parent);
43733                 if (testRange.inRange(range)) {
43734                     break;
43735                 }
43736                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43737                     break;
43738                 }
43739                 parent = parent.parentElement;
43740             }
43741             return parent;
43742         }
43743         
43744         // is ancestor a text element.
43745         var ac =  range.commonAncestorContainer;
43746         if (ac.nodeType == 3) {
43747             ac = ac.parentNode;
43748         }
43749         
43750         var ar = ac.childNodes;
43751          
43752         var nodes = [];
43753         var other_nodes = [];
43754         var has_other_nodes = false;
43755         for (var i=0;i<ar.length;i++) {
43756             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43757                 continue;
43758             }
43759             // fullly contained node.
43760             
43761             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43762                 nodes.push(ar[i]);
43763                 continue;
43764             }
43765             
43766             // probably selected..
43767             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43768                 other_nodes.push(ar[i]);
43769                 continue;
43770             }
43771             // outer..
43772             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43773                 continue;
43774             }
43775             
43776             
43777             has_other_nodes = true;
43778         }
43779         if (!nodes.length && other_nodes.length) {
43780             nodes= other_nodes;
43781         }
43782         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43783             return false;
43784         }
43785         
43786         return nodes[0];
43787     },
43788     createRange: function(sel)
43789     {
43790         // this has strange effects when using with 
43791         // top toolbar - not sure if it's a great idea.
43792         //this.editor.contentWindow.focus();
43793         if (typeof sel != "undefined") {
43794             try {
43795                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43796             } catch(e) {
43797                 return this.doc.createRange();
43798             }
43799         } else {
43800             return this.doc.createRange();
43801         }
43802     },
43803     getParentElement: function()
43804     {
43805         
43806         this.assignDocWin();
43807         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43808         
43809         var range = this.createRange(sel);
43810          
43811         try {
43812             var p = range.commonAncestorContainer;
43813             while (p.nodeType == 3) { // text node
43814                 p = p.parentNode;
43815             }
43816             return p;
43817         } catch (e) {
43818             return null;
43819         }
43820     
43821     },
43822     /***
43823      *
43824      * Range intersection.. the hard stuff...
43825      *  '-1' = before
43826      *  '0' = hits..
43827      *  '1' = after.
43828      *         [ -- selected range --- ]
43829      *   [fail]                        [fail]
43830      *
43831      *    basically..
43832      *      if end is before start or  hits it. fail.
43833      *      if start is after end or hits it fail.
43834      *
43835      *   if either hits (but other is outside. - then it's not 
43836      *   
43837      *    
43838      **/
43839     
43840     
43841     // @see http://www.thismuchiknow.co.uk/?p=64.
43842     rangeIntersectsNode : function(range, node)
43843     {
43844         var nodeRange = node.ownerDocument.createRange();
43845         try {
43846             nodeRange.selectNode(node);
43847         } catch (e) {
43848             nodeRange.selectNodeContents(node);
43849         }
43850     
43851         var rangeStartRange = range.cloneRange();
43852         rangeStartRange.collapse(true);
43853     
43854         var rangeEndRange = range.cloneRange();
43855         rangeEndRange.collapse(false);
43856     
43857         var nodeStartRange = nodeRange.cloneRange();
43858         nodeStartRange.collapse(true);
43859     
43860         var nodeEndRange = nodeRange.cloneRange();
43861         nodeEndRange.collapse(false);
43862     
43863         return rangeStartRange.compareBoundaryPoints(
43864                  Range.START_TO_START, nodeEndRange) == -1 &&
43865                rangeEndRange.compareBoundaryPoints(
43866                  Range.START_TO_START, nodeStartRange) == 1;
43867         
43868          
43869     },
43870     rangeCompareNode : function(range, node)
43871     {
43872         var nodeRange = node.ownerDocument.createRange();
43873         try {
43874             nodeRange.selectNode(node);
43875         } catch (e) {
43876             nodeRange.selectNodeContents(node);
43877         }
43878         
43879         
43880         range.collapse(true);
43881     
43882         nodeRange.collapse(true);
43883      
43884         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43885         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43886          
43887         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43888         
43889         var nodeIsBefore   =  ss == 1;
43890         var nodeIsAfter    = ee == -1;
43891         
43892         if (nodeIsBefore && nodeIsAfter) {
43893             return 0; // outer
43894         }
43895         if (!nodeIsBefore && nodeIsAfter) {
43896             return 1; //right trailed.
43897         }
43898         
43899         if (nodeIsBefore && !nodeIsAfter) {
43900             return 2;  // left trailed.
43901         }
43902         // fully contined.
43903         return 3;
43904     },
43905
43906     // private? - in a new class?
43907     cleanUpPaste :  function()
43908     {
43909         // cleans up the whole document..
43910         Roo.log('cleanuppaste');
43911         
43912         this.cleanUpChildren(this.doc.body);
43913         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43914         if (clean != this.doc.body.innerHTML) {
43915             this.doc.body.innerHTML = clean;
43916         }
43917         
43918     },
43919     
43920     cleanWordChars : function(input) {// change the chars to hex code
43921         var he = Roo.HtmlEditorCore;
43922         
43923         var output = input;
43924         Roo.each(he.swapCodes, function(sw) { 
43925             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43926             
43927             output = output.replace(swapper, sw[1]);
43928         });
43929         
43930         return output;
43931     },
43932     
43933     
43934     cleanUpChildren : function (n)
43935     {
43936         if (!n.childNodes.length) {
43937             return;
43938         }
43939         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43940            this.cleanUpChild(n.childNodes[i]);
43941         }
43942     },
43943     
43944     
43945         
43946     
43947     cleanUpChild : function (node)
43948     {
43949         var ed = this;
43950         //console.log(node);
43951         if (node.nodeName == "#text") {
43952             // clean up silly Windows -- stuff?
43953             return; 
43954         }
43955         if (node.nodeName == "#comment") {
43956             node.parentNode.removeChild(node);
43957             // clean up silly Windows -- stuff?
43958             return; 
43959         }
43960         var lcname = node.tagName.toLowerCase();
43961         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43962         // whitelist of tags..
43963         
43964         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43965             // remove node.
43966             node.parentNode.removeChild(node);
43967             return;
43968             
43969         }
43970         
43971         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43972         
43973         // spans with no attributes - just remove them..
43974         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
43975             remove_keep_children = true;
43976         }
43977         
43978         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43979         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43980         
43981         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43982         //    remove_keep_children = true;
43983         //}
43984         
43985         if (remove_keep_children) {
43986             this.cleanUpChildren(node);
43987             // inserts everything just before this node...
43988             while (node.childNodes.length) {
43989                 var cn = node.childNodes[0];
43990                 node.removeChild(cn);
43991                 node.parentNode.insertBefore(cn, node);
43992             }
43993             node.parentNode.removeChild(node);
43994             return;
43995         }
43996         
43997         if (!node.attributes || !node.attributes.length) {
43998             
43999           
44000             
44001             
44002             this.cleanUpChildren(node);
44003             return;
44004         }
44005         
44006         function cleanAttr(n,v)
44007         {
44008             
44009             if (v.match(/^\./) || v.match(/^\//)) {
44010                 return;
44011             }
44012             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44013                 return;
44014             }
44015             if (v.match(/^#/)) {
44016                 return;
44017             }
44018 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44019             node.removeAttribute(n);
44020             
44021         }
44022         
44023         var cwhite = this.cwhite;
44024         var cblack = this.cblack;
44025             
44026         function cleanStyle(n,v)
44027         {
44028             if (v.match(/expression/)) { //XSS?? should we even bother..
44029                 node.removeAttribute(n);
44030                 return;
44031             }
44032             
44033             var parts = v.split(/;/);
44034             var clean = [];
44035             
44036             Roo.each(parts, function(p) {
44037                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44038                 if (!p.length) {
44039                     return true;
44040                 }
44041                 var l = p.split(':').shift().replace(/\s+/g,'');
44042                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44043                 
44044                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44045 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44046                     //node.removeAttribute(n);
44047                     return true;
44048                 }
44049                 //Roo.log()
44050                 // only allow 'c whitelisted system attributes'
44051                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44052 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44053                     //node.removeAttribute(n);
44054                     return true;
44055                 }
44056                 
44057                 
44058                  
44059                 
44060                 clean.push(p);
44061                 return true;
44062             });
44063             if (clean.length) { 
44064                 node.setAttribute(n, clean.join(';'));
44065             } else {
44066                 node.removeAttribute(n);
44067             }
44068             
44069         }
44070         
44071         
44072         for (var i = node.attributes.length-1; i > -1 ; i--) {
44073             var a = node.attributes[i];
44074             //console.log(a);
44075             
44076             if (a.name.toLowerCase().substr(0,2)=='on')  {
44077                 node.removeAttribute(a.name);
44078                 continue;
44079             }
44080             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44081                 node.removeAttribute(a.name);
44082                 continue;
44083             }
44084             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44085                 cleanAttr(a.name,a.value); // fixme..
44086                 continue;
44087             }
44088             if (a.name == 'style') {
44089                 cleanStyle(a.name,a.value);
44090                 continue;
44091             }
44092             /// clean up MS crap..
44093             // tecnically this should be a list of valid class'es..
44094             
44095             
44096             if (a.name == 'class') {
44097                 if (a.value.match(/^Mso/)) {
44098                     node.removeAttribute('class');
44099                 }
44100                 
44101                 if (a.value.match(/^body$/)) {
44102                     node.removeAttribute('class');
44103                 }
44104                 continue;
44105             }
44106             
44107             // style cleanup!?
44108             // class cleanup?
44109             
44110         }
44111         
44112         
44113         this.cleanUpChildren(node);
44114         
44115         
44116     },
44117     
44118     /**
44119      * Clean up MS wordisms...
44120      */
44121     cleanWord : function(node)
44122     {
44123         if (!node) {
44124             this.cleanWord(this.doc.body);
44125             return;
44126         }
44127         
44128         if(
44129                 node.nodeName == 'SPAN' &&
44130                 !node.hasAttributes() &&
44131                 node.childNodes.length == 1 &&
44132                 node.firstChild.nodeName == "#text"  
44133         ) {
44134             var textNode = node.firstChild;
44135             node.removeChild(textNode);
44136             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44137                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44138             }
44139             node.parentNode.insertBefore(textNode, node);
44140             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44141                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44142             }
44143             node.parentNode.removeChild(node);
44144         }
44145         
44146         if (node.nodeName == "#text") {
44147             // clean up silly Windows -- stuff?
44148             return; 
44149         }
44150         if (node.nodeName == "#comment") {
44151             node.parentNode.removeChild(node);
44152             // clean up silly Windows -- stuff?
44153             return; 
44154         }
44155         
44156         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44157             node.parentNode.removeChild(node);
44158             return;
44159         }
44160         //Roo.log(node.tagName);
44161         // remove - but keep children..
44162         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44163             //Roo.log('-- removed');
44164             while (node.childNodes.length) {
44165                 var cn = node.childNodes[0];
44166                 node.removeChild(cn);
44167                 node.parentNode.insertBefore(cn, node);
44168                 // move node to parent - and clean it..
44169                 this.cleanWord(cn);
44170             }
44171             node.parentNode.removeChild(node);
44172             /// no need to iterate chidlren = it's got none..
44173             //this.iterateChildren(node, this.cleanWord);
44174             return;
44175         }
44176         // clean styles
44177         if (node.className.length) {
44178             
44179             var cn = node.className.split(/\W+/);
44180             var cna = [];
44181             Roo.each(cn, function(cls) {
44182                 if (cls.match(/Mso[a-zA-Z]+/)) {
44183                     return;
44184                 }
44185                 cna.push(cls);
44186             });
44187             node.className = cna.length ? cna.join(' ') : '';
44188             if (!cna.length) {
44189                 node.removeAttribute("class");
44190             }
44191         }
44192         
44193         if (node.hasAttribute("lang")) {
44194             node.removeAttribute("lang");
44195         }
44196         
44197         if (node.hasAttribute("style")) {
44198             
44199             var styles = node.getAttribute("style").split(";");
44200             var nstyle = [];
44201             Roo.each(styles, function(s) {
44202                 if (!s.match(/:/)) {
44203                     return;
44204                 }
44205                 var kv = s.split(":");
44206                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44207                     return;
44208                 }
44209                 // what ever is left... we allow.
44210                 nstyle.push(s);
44211             });
44212             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44213             if (!nstyle.length) {
44214                 node.removeAttribute('style');
44215             }
44216         }
44217         this.iterateChildren(node, this.cleanWord);
44218         
44219         
44220         
44221     },
44222     /**
44223      * iterateChildren of a Node, calling fn each time, using this as the scole..
44224      * @param {DomNode} node node to iterate children of.
44225      * @param {Function} fn method of this class to call on each item.
44226      */
44227     iterateChildren : function(node, fn)
44228     {
44229         if (!node.childNodes.length) {
44230                 return;
44231         }
44232         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44233            fn.call(this, node.childNodes[i])
44234         }
44235     },
44236     
44237     
44238     /**
44239      * cleanTableWidths.
44240      *
44241      * Quite often pasting from word etc.. results in tables with column and widths.
44242      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44243      *
44244      */
44245     cleanTableWidths : function(node)
44246     {
44247          
44248          
44249         if (!node) {
44250             this.cleanTableWidths(this.doc.body);
44251             return;
44252         }
44253         
44254         // ignore list...
44255         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44256             return; 
44257         }
44258         Roo.log(node.tagName);
44259         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44260             this.iterateChildren(node, this.cleanTableWidths);
44261             return;
44262         }
44263         if (node.hasAttribute('width')) {
44264             node.removeAttribute('width');
44265         }
44266         
44267          
44268         if (node.hasAttribute("style")) {
44269             // pretty basic...
44270             
44271             var styles = node.getAttribute("style").split(";");
44272             var nstyle = [];
44273             Roo.each(styles, function(s) {
44274                 if (!s.match(/:/)) {
44275                     return;
44276                 }
44277                 var kv = s.split(":");
44278                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44279                     return;
44280                 }
44281                 // what ever is left... we allow.
44282                 nstyle.push(s);
44283             });
44284             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44285             if (!nstyle.length) {
44286                 node.removeAttribute('style');
44287             }
44288         }
44289         
44290         this.iterateChildren(node, this.cleanTableWidths);
44291         
44292         
44293     },
44294     
44295     
44296     
44297     
44298     domToHTML : function(currentElement, depth, nopadtext) {
44299         
44300         depth = depth || 0;
44301         nopadtext = nopadtext || false;
44302     
44303         if (!currentElement) {
44304             return this.domToHTML(this.doc.body);
44305         }
44306         
44307         //Roo.log(currentElement);
44308         var j;
44309         var allText = false;
44310         var nodeName = currentElement.nodeName;
44311         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44312         
44313         if  (nodeName == '#text') {
44314             
44315             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44316         }
44317         
44318         
44319         var ret = '';
44320         if (nodeName != 'BODY') {
44321              
44322             var i = 0;
44323             // Prints the node tagName, such as <A>, <IMG>, etc
44324             if (tagName) {
44325                 var attr = [];
44326                 for(i = 0; i < currentElement.attributes.length;i++) {
44327                     // quoting?
44328                     var aname = currentElement.attributes.item(i).name;
44329                     if (!currentElement.attributes.item(i).value.length) {
44330                         continue;
44331                     }
44332                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44333                 }
44334                 
44335                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44336             } 
44337             else {
44338                 
44339                 // eack
44340             }
44341         } else {
44342             tagName = false;
44343         }
44344         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44345             return ret;
44346         }
44347         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44348             nopadtext = true;
44349         }
44350         
44351         
44352         // Traverse the tree
44353         i = 0;
44354         var currentElementChild = currentElement.childNodes.item(i);
44355         var allText = true;
44356         var innerHTML  = '';
44357         lastnode = '';
44358         while (currentElementChild) {
44359             // Formatting code (indent the tree so it looks nice on the screen)
44360             var nopad = nopadtext;
44361             if (lastnode == 'SPAN') {
44362                 nopad  = true;
44363             }
44364             // text
44365             if  (currentElementChild.nodeName == '#text') {
44366                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44367                 toadd = nopadtext ? toadd : toadd.trim();
44368                 if (!nopad && toadd.length > 80) {
44369                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44370                 }
44371                 innerHTML  += toadd;
44372                 
44373                 i++;
44374                 currentElementChild = currentElement.childNodes.item(i);
44375                 lastNode = '';
44376                 continue;
44377             }
44378             allText = false;
44379             
44380             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44381                 
44382             // Recursively traverse the tree structure of the child node
44383             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44384             lastnode = currentElementChild.nodeName;
44385             i++;
44386             currentElementChild=currentElement.childNodes.item(i);
44387         }
44388         
44389         ret += innerHTML;
44390         
44391         if (!allText) {
44392                 // The remaining code is mostly for formatting the tree
44393             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44394         }
44395         
44396         
44397         if (tagName) {
44398             ret+= "</"+tagName+">";
44399         }
44400         return ret;
44401         
44402     },
44403         
44404     applyBlacklists : function()
44405     {
44406         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44407         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44408         
44409         this.white = [];
44410         this.black = [];
44411         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44412             if (b.indexOf(tag) > -1) {
44413                 return;
44414             }
44415             this.white.push(tag);
44416             
44417         }, this);
44418         
44419         Roo.each(w, function(tag) {
44420             if (b.indexOf(tag) > -1) {
44421                 return;
44422             }
44423             if (this.white.indexOf(tag) > -1) {
44424                 return;
44425             }
44426             this.white.push(tag);
44427             
44428         }, this);
44429         
44430         
44431         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44432             if (w.indexOf(tag) > -1) {
44433                 return;
44434             }
44435             this.black.push(tag);
44436             
44437         }, this);
44438         
44439         Roo.each(b, function(tag) {
44440             if (w.indexOf(tag) > -1) {
44441                 return;
44442             }
44443             if (this.black.indexOf(tag) > -1) {
44444                 return;
44445             }
44446             this.black.push(tag);
44447             
44448         }, this);
44449         
44450         
44451         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44452         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44453         
44454         this.cwhite = [];
44455         this.cblack = [];
44456         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44457             if (b.indexOf(tag) > -1) {
44458                 return;
44459             }
44460             this.cwhite.push(tag);
44461             
44462         }, this);
44463         
44464         Roo.each(w, function(tag) {
44465             if (b.indexOf(tag) > -1) {
44466                 return;
44467             }
44468             if (this.cwhite.indexOf(tag) > -1) {
44469                 return;
44470             }
44471             this.cwhite.push(tag);
44472             
44473         }, this);
44474         
44475         
44476         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44477             if (w.indexOf(tag) > -1) {
44478                 return;
44479             }
44480             this.cblack.push(tag);
44481             
44482         }, this);
44483         
44484         Roo.each(b, function(tag) {
44485             if (w.indexOf(tag) > -1) {
44486                 return;
44487             }
44488             if (this.cblack.indexOf(tag) > -1) {
44489                 return;
44490             }
44491             this.cblack.push(tag);
44492             
44493         }, this);
44494     },
44495     
44496     setStylesheets : function(stylesheets)
44497     {
44498         if(typeof(stylesheets) == 'string'){
44499             Roo.get(this.iframe.contentDocument.head).createChild({
44500                 tag : 'link',
44501                 rel : 'stylesheet',
44502                 type : 'text/css',
44503                 href : stylesheets
44504             });
44505             
44506             return;
44507         }
44508         var _this = this;
44509      
44510         Roo.each(stylesheets, function(s) {
44511             if(!s.length){
44512                 return;
44513             }
44514             
44515             Roo.get(_this.iframe.contentDocument.head).createChild({
44516                 tag : 'link',
44517                 rel : 'stylesheet',
44518                 type : 'text/css',
44519                 href : s
44520             });
44521         });
44522
44523         
44524     },
44525     
44526     removeStylesheets : function()
44527     {
44528         var _this = this;
44529         
44530         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44531             s.remove();
44532         });
44533     },
44534     
44535     setStyle : function(style)
44536     {
44537         Roo.get(this.iframe.contentDocument.head).createChild({
44538             tag : 'style',
44539             type : 'text/css',
44540             html : style
44541         });
44542
44543         return;
44544     }
44545     
44546     // hide stuff that is not compatible
44547     /**
44548      * @event blur
44549      * @hide
44550      */
44551     /**
44552      * @event change
44553      * @hide
44554      */
44555     /**
44556      * @event focus
44557      * @hide
44558      */
44559     /**
44560      * @event specialkey
44561      * @hide
44562      */
44563     /**
44564      * @cfg {String} fieldClass @hide
44565      */
44566     /**
44567      * @cfg {String} focusClass @hide
44568      */
44569     /**
44570      * @cfg {String} autoCreate @hide
44571      */
44572     /**
44573      * @cfg {String} inputType @hide
44574      */
44575     /**
44576      * @cfg {String} invalidClass @hide
44577      */
44578     /**
44579      * @cfg {String} invalidText @hide
44580      */
44581     /**
44582      * @cfg {String} msgFx @hide
44583      */
44584     /**
44585      * @cfg {String} validateOnBlur @hide
44586      */
44587 });
44588
44589 Roo.HtmlEditorCore.white = [
44590         'area', 'br', 'img', 'input', 'hr', 'wbr',
44591         
44592        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44593        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44594        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44595        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44596        'table',   'ul',         'xmp', 
44597        
44598        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44599       'thead',   'tr', 
44600      
44601       'dir', 'menu', 'ol', 'ul', 'dl',
44602        
44603       'embed',  'object'
44604 ];
44605
44606
44607 Roo.HtmlEditorCore.black = [
44608     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44609         'applet', // 
44610         'base',   'basefont', 'bgsound', 'blink',  'body', 
44611         'frame',  'frameset', 'head',    'html',   'ilayer', 
44612         'iframe', 'layer',  'link',     'meta',    'object',   
44613         'script', 'style' ,'title',  'xml' // clean later..
44614 ];
44615 Roo.HtmlEditorCore.clean = [
44616     'script', 'style', 'title', 'xml'
44617 ];
44618 Roo.HtmlEditorCore.remove = [
44619     'font'
44620 ];
44621 // attributes..
44622
44623 Roo.HtmlEditorCore.ablack = [
44624     'on'
44625 ];
44626     
44627 Roo.HtmlEditorCore.aclean = [ 
44628     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44629 ];
44630
44631 // protocols..
44632 Roo.HtmlEditorCore.pwhite= [
44633         'http',  'https',  'mailto'
44634 ];
44635
44636 // white listed style attributes.
44637 Roo.HtmlEditorCore.cwhite= [
44638       //  'text-align', /// default is to allow most things..
44639       
44640          
44641 //        'font-size'//??
44642 ];
44643
44644 // black listed style attributes.
44645 Roo.HtmlEditorCore.cblack= [
44646       //  'font-size' -- this can be set by the project 
44647 ];
44648
44649
44650 Roo.HtmlEditorCore.swapCodes   =[ 
44651     [    8211, "--" ], 
44652     [    8212, "--" ], 
44653     [    8216,  "'" ],  
44654     [    8217, "'" ],  
44655     [    8220, '"' ],  
44656     [    8221, '"' ],  
44657     [    8226, "*" ],  
44658     [    8230, "..." ]
44659 ]; 
44660
44661     //<script type="text/javascript">
44662
44663 /*
44664  * Ext JS Library 1.1.1
44665  * Copyright(c) 2006-2007, Ext JS, LLC.
44666  * Licence LGPL
44667  * 
44668  */
44669  
44670  
44671 Roo.form.HtmlEditor = function(config){
44672     
44673     
44674     
44675     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44676     
44677     if (!this.toolbars) {
44678         this.toolbars = [];
44679     }
44680     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44681     
44682     
44683 };
44684
44685 /**
44686  * @class Roo.form.HtmlEditor
44687  * @extends Roo.form.Field
44688  * Provides a lightweight HTML Editor component.
44689  *
44690  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44691  * 
44692  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44693  * supported by this editor.</b><br/><br/>
44694  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44695  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44696  */
44697 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44698     /**
44699      * @cfg {Boolean} clearUp
44700      */
44701     clearUp : true,
44702       /**
44703      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44704      */
44705     toolbars : false,
44706    
44707      /**
44708      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44709      *                        Roo.resizable.
44710      */
44711     resizable : false,
44712      /**
44713      * @cfg {Number} height (in pixels)
44714      */   
44715     height: 300,
44716    /**
44717      * @cfg {Number} width (in pixels)
44718      */   
44719     width: 500,
44720     
44721     /**
44722      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44723      * 
44724      */
44725     stylesheets: false,
44726     
44727     
44728      /**
44729      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44730      * 
44731      */
44732     cblack: false,
44733     /**
44734      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44735      * 
44736      */
44737     cwhite: false,
44738     
44739      /**
44740      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44741      * 
44742      */
44743     black: false,
44744     /**
44745      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44746      * 
44747      */
44748     white: false,
44749     
44750     // id of frame..
44751     frameId: false,
44752     
44753     // private properties
44754     validationEvent : false,
44755     deferHeight: true,
44756     initialized : false,
44757     activated : false,
44758     
44759     onFocus : Roo.emptyFn,
44760     iframePad:3,
44761     hideMode:'offsets',
44762     
44763     actionMode : 'container', // defaults to hiding it...
44764     
44765     defaultAutoCreate : { // modified by initCompnoent..
44766         tag: "textarea",
44767         style:"width:500px;height:300px;",
44768         autocomplete: "new-password"
44769     },
44770
44771     // private
44772     initComponent : function(){
44773         this.addEvents({
44774             /**
44775              * @event initialize
44776              * Fires when the editor is fully initialized (including the iframe)
44777              * @param {HtmlEditor} this
44778              */
44779             initialize: true,
44780             /**
44781              * @event activate
44782              * Fires when the editor is first receives the focus. Any insertion must wait
44783              * until after this event.
44784              * @param {HtmlEditor} this
44785              */
44786             activate: true,
44787              /**
44788              * @event beforesync
44789              * Fires before the textarea is updated with content from the editor iframe. Return false
44790              * to cancel the sync.
44791              * @param {HtmlEditor} this
44792              * @param {String} html
44793              */
44794             beforesync: true,
44795              /**
44796              * @event beforepush
44797              * Fires before the iframe editor is updated with content from the textarea. Return false
44798              * to cancel the push.
44799              * @param {HtmlEditor} this
44800              * @param {String} html
44801              */
44802             beforepush: true,
44803              /**
44804              * @event sync
44805              * Fires when the textarea is updated with content from the editor iframe.
44806              * @param {HtmlEditor} this
44807              * @param {String} html
44808              */
44809             sync: true,
44810              /**
44811              * @event push
44812              * Fires when the iframe editor is updated with content from the textarea.
44813              * @param {HtmlEditor} this
44814              * @param {String} html
44815              */
44816             push: true,
44817              /**
44818              * @event editmodechange
44819              * Fires when the editor switches edit modes
44820              * @param {HtmlEditor} this
44821              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44822              */
44823             editmodechange: true,
44824             /**
44825              * @event editorevent
44826              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44827              * @param {HtmlEditor} this
44828              */
44829             editorevent: true,
44830             /**
44831              * @event firstfocus
44832              * Fires when on first focus - needed by toolbars..
44833              * @param {HtmlEditor} this
44834              */
44835             firstfocus: true,
44836             /**
44837              * @event autosave
44838              * Auto save the htmlEditor value as a file into Events
44839              * @param {HtmlEditor} this
44840              */
44841             autosave: true,
44842             /**
44843              * @event savedpreview
44844              * preview the saved version of htmlEditor
44845              * @param {HtmlEditor} this
44846              */
44847             savedpreview: true,
44848             
44849             /**
44850             * @event stylesheetsclick
44851             * Fires when press the Sytlesheets button
44852             * @param {Roo.HtmlEditorCore} this
44853             */
44854             stylesheetsclick: true
44855         });
44856         this.defaultAutoCreate =  {
44857             tag: "textarea",
44858             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44859             autocomplete: "new-password"
44860         };
44861     },
44862
44863     /**
44864      * Protected method that will not generally be called directly. It
44865      * is called when the editor creates its toolbar. Override this method if you need to
44866      * add custom toolbar buttons.
44867      * @param {HtmlEditor} editor
44868      */
44869     createToolbar : function(editor){
44870         Roo.log("create toolbars");
44871         if (!editor.toolbars || !editor.toolbars.length) {
44872             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44873         }
44874         
44875         for (var i =0 ; i < editor.toolbars.length;i++) {
44876             editor.toolbars[i] = Roo.factory(
44877                     typeof(editor.toolbars[i]) == 'string' ?
44878                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44879                 Roo.form.HtmlEditor);
44880             editor.toolbars[i].init(editor);
44881         }
44882          
44883         
44884     },
44885
44886      
44887     // private
44888     onRender : function(ct, position)
44889     {
44890         var _t = this;
44891         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44892         
44893         this.wrap = this.el.wrap({
44894             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44895         });
44896         
44897         this.editorcore.onRender(ct, position);
44898          
44899         if (this.resizable) {
44900             this.resizeEl = new Roo.Resizable(this.wrap, {
44901                 pinned : true,
44902                 wrap: true,
44903                 dynamic : true,
44904                 minHeight : this.height,
44905                 height: this.height,
44906                 handles : this.resizable,
44907                 width: this.width,
44908                 listeners : {
44909                     resize : function(r, w, h) {
44910                         _t.onResize(w,h); // -something
44911                     }
44912                 }
44913             });
44914             
44915         }
44916         this.createToolbar(this);
44917        
44918         
44919         if(!this.width){
44920             this.setSize(this.wrap.getSize());
44921         }
44922         if (this.resizeEl) {
44923             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44924             // should trigger onReize..
44925         }
44926         
44927         this.keyNav = new Roo.KeyNav(this.el, {
44928             
44929             "tab" : function(e){
44930                 e.preventDefault();
44931                 
44932                 var value = this.getValue();
44933                 
44934                 var start = this.el.dom.selectionStart;
44935                 var end = this.el.dom.selectionEnd;
44936                 
44937                 if(!e.shiftKey){
44938                     
44939                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44940                     this.el.dom.setSelectionRange(end + 1, end + 1);
44941                     return;
44942                 }
44943                 
44944                 var f = value.substring(0, start).split("\t");
44945                 
44946                 if(f.pop().length != 0){
44947                     return;
44948                 }
44949                 
44950                 this.setValue(f.join("\t") + value.substring(end));
44951                 this.el.dom.setSelectionRange(start - 1, start - 1);
44952                 
44953             },
44954             
44955             "home" : function(e){
44956                 e.preventDefault();
44957                 
44958                 var curr = this.el.dom.selectionStart;
44959                 var lines = this.getValue().split("\n");
44960                 
44961                 if(!lines.length){
44962                     return;
44963                 }
44964                 
44965                 if(e.ctrlKey){
44966                     this.el.dom.setSelectionRange(0, 0);
44967                     return;
44968                 }
44969                 
44970                 var pos = 0;
44971                 
44972                 for (var i = 0; i < lines.length;i++) {
44973                     pos += lines[i].length;
44974                     
44975                     if(i != 0){
44976                         pos += 1;
44977                     }
44978                     
44979                     if(pos < curr){
44980                         continue;
44981                     }
44982                     
44983                     pos -= lines[i].length;
44984                     
44985                     break;
44986                 }
44987                 
44988                 if(!e.shiftKey){
44989                     this.el.dom.setSelectionRange(pos, pos);
44990                     return;
44991                 }
44992                 
44993                 this.el.dom.selectionStart = pos;
44994                 this.el.dom.selectionEnd = curr;
44995             },
44996             
44997             "end" : function(e){
44998                 e.preventDefault();
44999                 
45000                 var curr = this.el.dom.selectionStart;
45001                 var lines = this.getValue().split("\n");
45002                 
45003                 if(!lines.length){
45004                     return;
45005                 }
45006                 
45007                 if(e.ctrlKey){
45008                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45009                     return;
45010                 }
45011                 
45012                 var pos = 0;
45013                 
45014                 for (var i = 0; i < lines.length;i++) {
45015                     
45016                     pos += lines[i].length;
45017                     
45018                     if(i != 0){
45019                         pos += 1;
45020                     }
45021                     
45022                     if(pos < curr){
45023                         continue;
45024                     }
45025                     
45026                     break;
45027                 }
45028                 
45029                 if(!e.shiftKey){
45030                     this.el.dom.setSelectionRange(pos, pos);
45031                     return;
45032                 }
45033                 
45034                 this.el.dom.selectionStart = curr;
45035                 this.el.dom.selectionEnd = pos;
45036             },
45037
45038             scope : this,
45039
45040             doRelay : function(foo, bar, hname){
45041                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45042             },
45043
45044             forceKeyDown: true
45045         });
45046         
45047 //        if(this.autosave && this.w){
45048 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45049 //        }
45050     },
45051
45052     // private
45053     onResize : function(w, h)
45054     {
45055         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45056         var ew = false;
45057         var eh = false;
45058         
45059         if(this.el ){
45060             if(typeof w == 'number'){
45061                 var aw = w - this.wrap.getFrameWidth('lr');
45062                 this.el.setWidth(this.adjustWidth('textarea', aw));
45063                 ew = aw;
45064             }
45065             if(typeof h == 'number'){
45066                 var tbh = 0;
45067                 for (var i =0; i < this.toolbars.length;i++) {
45068                     // fixme - ask toolbars for heights?
45069                     tbh += this.toolbars[i].tb.el.getHeight();
45070                     if (this.toolbars[i].footer) {
45071                         tbh += this.toolbars[i].footer.el.getHeight();
45072                     }
45073                 }
45074                 
45075                 
45076                 
45077                 
45078                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45079                 ah -= 5; // knock a few pixes off for look..
45080 //                Roo.log(ah);
45081                 this.el.setHeight(this.adjustWidth('textarea', ah));
45082                 var eh = ah;
45083             }
45084         }
45085         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45086         this.editorcore.onResize(ew,eh);
45087         
45088     },
45089
45090     /**
45091      * Toggles the editor between standard and source edit mode.
45092      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45093      */
45094     toggleSourceEdit : function(sourceEditMode)
45095     {
45096         this.editorcore.toggleSourceEdit(sourceEditMode);
45097         
45098         if(this.editorcore.sourceEditMode){
45099             Roo.log('editor - showing textarea');
45100             
45101 //            Roo.log('in');
45102 //            Roo.log(this.syncValue());
45103             this.editorcore.syncValue();
45104             this.el.removeClass('x-hidden');
45105             this.el.dom.removeAttribute('tabIndex');
45106             this.el.focus();
45107             
45108             for (var i = 0; i < this.toolbars.length; i++) {
45109                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45110                     this.toolbars[i].tb.hide();
45111                     this.toolbars[i].footer.hide();
45112                 }
45113             }
45114             
45115         }else{
45116             Roo.log('editor - hiding textarea');
45117 //            Roo.log('out')
45118 //            Roo.log(this.pushValue()); 
45119             this.editorcore.pushValue();
45120             
45121             this.el.addClass('x-hidden');
45122             this.el.dom.setAttribute('tabIndex', -1);
45123             
45124             for (var i = 0; i < this.toolbars.length; i++) {
45125                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45126                     this.toolbars[i].tb.show();
45127                     this.toolbars[i].footer.show();
45128                 }
45129             }
45130             
45131             //this.deferFocus();
45132         }
45133         
45134         this.setSize(this.wrap.getSize());
45135         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45136         
45137         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45138     },
45139  
45140     // private (for BoxComponent)
45141     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45142
45143     // private (for BoxComponent)
45144     getResizeEl : function(){
45145         return this.wrap;
45146     },
45147
45148     // private (for BoxComponent)
45149     getPositionEl : function(){
45150         return this.wrap;
45151     },
45152
45153     // private
45154     initEvents : function(){
45155         this.originalValue = this.getValue();
45156     },
45157
45158     /**
45159      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45160      * @method
45161      */
45162     markInvalid : Roo.emptyFn,
45163     /**
45164      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45165      * @method
45166      */
45167     clearInvalid : Roo.emptyFn,
45168
45169     setValue : function(v){
45170         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45171         this.editorcore.pushValue();
45172     },
45173
45174      
45175     // private
45176     deferFocus : function(){
45177         this.focus.defer(10, this);
45178     },
45179
45180     // doc'ed in Field
45181     focus : function(){
45182         this.editorcore.focus();
45183         
45184     },
45185       
45186
45187     // private
45188     onDestroy : function(){
45189         
45190         
45191         
45192         if(this.rendered){
45193             
45194             for (var i =0; i < this.toolbars.length;i++) {
45195                 // fixme - ask toolbars for heights?
45196                 this.toolbars[i].onDestroy();
45197             }
45198             
45199             this.wrap.dom.innerHTML = '';
45200             this.wrap.remove();
45201         }
45202     },
45203
45204     // private
45205     onFirstFocus : function(){
45206         //Roo.log("onFirstFocus");
45207         this.editorcore.onFirstFocus();
45208          for (var i =0; i < this.toolbars.length;i++) {
45209             this.toolbars[i].onFirstFocus();
45210         }
45211         
45212     },
45213     
45214     // private
45215     syncValue : function()
45216     {
45217         this.editorcore.syncValue();
45218     },
45219     
45220     pushValue : function()
45221     {
45222         this.editorcore.pushValue();
45223     },
45224     
45225     setStylesheets : function(stylesheets)
45226     {
45227         this.editorcore.setStylesheets(stylesheets);
45228     },
45229     
45230     removeStylesheets : function()
45231     {
45232         this.editorcore.removeStylesheets();
45233     }
45234      
45235     
45236     // hide stuff that is not compatible
45237     /**
45238      * @event blur
45239      * @hide
45240      */
45241     /**
45242      * @event change
45243      * @hide
45244      */
45245     /**
45246      * @event focus
45247      * @hide
45248      */
45249     /**
45250      * @event specialkey
45251      * @hide
45252      */
45253     /**
45254      * @cfg {String} fieldClass @hide
45255      */
45256     /**
45257      * @cfg {String} focusClass @hide
45258      */
45259     /**
45260      * @cfg {String} autoCreate @hide
45261      */
45262     /**
45263      * @cfg {String} inputType @hide
45264      */
45265     /**
45266      * @cfg {String} invalidClass @hide
45267      */
45268     /**
45269      * @cfg {String} invalidText @hide
45270      */
45271     /**
45272      * @cfg {String} msgFx @hide
45273      */
45274     /**
45275      * @cfg {String} validateOnBlur @hide
45276      */
45277 });
45278  
45279     // <script type="text/javascript">
45280 /*
45281  * Based on
45282  * Ext JS Library 1.1.1
45283  * Copyright(c) 2006-2007, Ext JS, LLC.
45284  *  
45285  
45286  */
45287
45288 /**
45289  * @class Roo.form.HtmlEditorToolbar1
45290  * Basic Toolbar
45291  * 
45292  * Usage:
45293  *
45294  new Roo.form.HtmlEditor({
45295     ....
45296     toolbars : [
45297         new Roo.form.HtmlEditorToolbar1({
45298             disable : { fonts: 1 , format: 1, ..., ... , ...],
45299             btns : [ .... ]
45300         })
45301     }
45302      
45303  * 
45304  * @cfg {Object} disable List of elements to disable..
45305  * @cfg {Array} btns List of additional buttons.
45306  * 
45307  * 
45308  * NEEDS Extra CSS? 
45309  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45310  */
45311  
45312 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45313 {
45314     
45315     Roo.apply(this, config);
45316     
45317     // default disabled, based on 'good practice'..
45318     this.disable = this.disable || {};
45319     Roo.applyIf(this.disable, {
45320         fontSize : true,
45321         colors : true,
45322         specialElements : true
45323     });
45324     
45325     
45326     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45327     // dont call parent... till later.
45328 }
45329
45330 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45331     
45332     tb: false,
45333     
45334     rendered: false,
45335     
45336     editor : false,
45337     editorcore : false,
45338     /**
45339      * @cfg {Object} disable  List of toolbar elements to disable
45340          
45341      */
45342     disable : false,
45343     
45344     
45345      /**
45346      * @cfg {String} createLinkText The default text for the create link prompt
45347      */
45348     createLinkText : 'Please enter the URL for the link:',
45349     /**
45350      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45351      */
45352     defaultLinkValue : 'http:/'+'/',
45353    
45354     
45355       /**
45356      * @cfg {Array} fontFamilies An array of available font families
45357      */
45358     fontFamilies : [
45359         'Arial',
45360         'Courier New',
45361         'Tahoma',
45362         'Times New Roman',
45363         'Verdana'
45364     ],
45365     
45366     specialChars : [
45367            "&#169;",
45368           "&#174;",     
45369           "&#8482;",    
45370           "&#163;" ,    
45371          // "&#8212;",    
45372           "&#8230;",    
45373           "&#247;" ,    
45374         //  "&#225;" ,     ?? a acute?
45375            "&#8364;"    , //Euro
45376        //   "&#8220;"    ,
45377         //  "&#8221;"    ,
45378         //  "&#8226;"    ,
45379           "&#176;"  //   , // degrees
45380
45381          // "&#233;"     , // e ecute
45382          // "&#250;"     , // u ecute?
45383     ],
45384     
45385     specialElements : [
45386         {
45387             text: "Insert Table",
45388             xtype: 'MenuItem',
45389             xns : Roo.Menu,
45390             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45391                 
45392         },
45393         {    
45394             text: "Insert Image",
45395             xtype: 'MenuItem',
45396             xns : Roo.Menu,
45397             ihtml : '<img src="about:blank"/>'
45398             
45399         }
45400         
45401          
45402     ],
45403     
45404     
45405     inputElements : [ 
45406             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45407             "input:submit", "input:button", "select", "textarea", "label" ],
45408     formats : [
45409         ["p"] ,  
45410         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45411         ["pre"],[ "code"], 
45412         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45413         ['div'],['span'],
45414         ['sup'],['sub']
45415     ],
45416     
45417     cleanStyles : [
45418         "font-size"
45419     ],
45420      /**
45421      * @cfg {String} defaultFont default font to use.
45422      */
45423     defaultFont: 'tahoma',
45424    
45425     fontSelect : false,
45426     
45427     
45428     formatCombo : false,
45429     
45430     init : function(editor)
45431     {
45432         this.editor = editor;
45433         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45434         var editorcore = this.editorcore;
45435         
45436         var _t = this;
45437         
45438         var fid = editorcore.frameId;
45439         var etb = this;
45440         function btn(id, toggle, handler){
45441             var xid = fid + '-'+ id ;
45442             return {
45443                 id : xid,
45444                 cmd : id,
45445                 cls : 'x-btn-icon x-edit-'+id,
45446                 enableToggle:toggle !== false,
45447                 scope: _t, // was editor...
45448                 handler:handler||_t.relayBtnCmd,
45449                 clickEvent:'mousedown',
45450                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45451                 tabIndex:-1
45452             };
45453         }
45454         
45455         
45456         
45457         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45458         this.tb = tb;
45459          // stop form submits
45460         tb.el.on('click', function(e){
45461             e.preventDefault(); // what does this do?
45462         });
45463
45464         if(!this.disable.font) { // && !Roo.isSafari){
45465             /* why no safari for fonts 
45466             editor.fontSelect = tb.el.createChild({
45467                 tag:'select',
45468                 tabIndex: -1,
45469                 cls:'x-font-select',
45470                 html: this.createFontOptions()
45471             });
45472             
45473             editor.fontSelect.on('change', function(){
45474                 var font = editor.fontSelect.dom.value;
45475                 editor.relayCmd('fontname', font);
45476                 editor.deferFocus();
45477             }, editor);
45478             
45479             tb.add(
45480                 editor.fontSelect.dom,
45481                 '-'
45482             );
45483             */
45484             
45485         };
45486         if(!this.disable.formats){
45487             this.formatCombo = new Roo.form.ComboBox({
45488                 store: new Roo.data.SimpleStore({
45489                     id : 'tag',
45490                     fields: ['tag'],
45491                     data : this.formats // from states.js
45492                 }),
45493                 blockFocus : true,
45494                 name : '',
45495                 //autoCreate : {tag: "div",  size: "20"},
45496                 displayField:'tag',
45497                 typeAhead: false,
45498                 mode: 'local',
45499                 editable : false,
45500                 triggerAction: 'all',
45501                 emptyText:'Add tag',
45502                 selectOnFocus:true,
45503                 width:135,
45504                 listeners : {
45505                     'select': function(c, r, i) {
45506                         editorcore.insertTag(r.get('tag'));
45507                         editor.focus();
45508                     }
45509                 }
45510
45511             });
45512             tb.addField(this.formatCombo);
45513             
45514         }
45515         
45516         if(!this.disable.format){
45517             tb.add(
45518                 btn('bold'),
45519                 btn('italic'),
45520                 btn('underline'),
45521                 btn('strikethrough')
45522             );
45523         };
45524         if(!this.disable.fontSize){
45525             tb.add(
45526                 '-',
45527                 
45528                 
45529                 btn('increasefontsize', false, editorcore.adjustFont),
45530                 btn('decreasefontsize', false, editorcore.adjustFont)
45531             );
45532         };
45533         
45534         
45535         if(!this.disable.colors){
45536             tb.add(
45537                 '-', {
45538                     id:editorcore.frameId +'-forecolor',
45539                     cls:'x-btn-icon x-edit-forecolor',
45540                     clickEvent:'mousedown',
45541                     tooltip: this.buttonTips['forecolor'] || undefined,
45542                     tabIndex:-1,
45543                     menu : new Roo.menu.ColorMenu({
45544                         allowReselect: true,
45545                         focus: Roo.emptyFn,
45546                         value:'000000',
45547                         plain:true,
45548                         selectHandler: function(cp, color){
45549                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45550                             editor.deferFocus();
45551                         },
45552                         scope: editorcore,
45553                         clickEvent:'mousedown'
45554                     })
45555                 }, {
45556                     id:editorcore.frameId +'backcolor',
45557                     cls:'x-btn-icon x-edit-backcolor',
45558                     clickEvent:'mousedown',
45559                     tooltip: this.buttonTips['backcolor'] || undefined,
45560                     tabIndex:-1,
45561                     menu : new Roo.menu.ColorMenu({
45562                         focus: Roo.emptyFn,
45563                         value:'FFFFFF',
45564                         plain:true,
45565                         allowReselect: true,
45566                         selectHandler: function(cp, color){
45567                             if(Roo.isGecko){
45568                                 editorcore.execCmd('useCSS', false);
45569                                 editorcore.execCmd('hilitecolor', color);
45570                                 editorcore.execCmd('useCSS', true);
45571                                 editor.deferFocus();
45572                             }else{
45573                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45574                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45575                                 editor.deferFocus();
45576                             }
45577                         },
45578                         scope:editorcore,
45579                         clickEvent:'mousedown'
45580                     })
45581                 }
45582             );
45583         };
45584         // now add all the items...
45585         
45586
45587         if(!this.disable.alignments){
45588             tb.add(
45589                 '-',
45590                 btn('justifyleft'),
45591                 btn('justifycenter'),
45592                 btn('justifyright')
45593             );
45594         };
45595
45596         //if(!Roo.isSafari){
45597             if(!this.disable.links){
45598                 tb.add(
45599                     '-',
45600                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45601                 );
45602             };
45603
45604             if(!this.disable.lists){
45605                 tb.add(
45606                     '-',
45607                     btn('insertorderedlist'),
45608                     btn('insertunorderedlist')
45609                 );
45610             }
45611             if(!this.disable.sourceEdit){
45612                 tb.add(
45613                     '-',
45614                     btn('sourceedit', true, function(btn){
45615                         this.toggleSourceEdit(btn.pressed);
45616                     })
45617                 );
45618             }
45619         //}
45620         
45621         var smenu = { };
45622         // special menu.. - needs to be tidied up..
45623         if (!this.disable.special) {
45624             smenu = {
45625                 text: "&#169;",
45626                 cls: 'x-edit-none',
45627                 
45628                 menu : {
45629                     items : []
45630                 }
45631             };
45632             for (var i =0; i < this.specialChars.length; i++) {
45633                 smenu.menu.items.push({
45634                     
45635                     html: this.specialChars[i],
45636                     handler: function(a,b) {
45637                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45638                         //editor.insertAtCursor(a.html);
45639                         
45640                     },
45641                     tabIndex:-1
45642                 });
45643             }
45644             
45645             
45646             tb.add(smenu);
45647             
45648             
45649         }
45650         
45651         var cmenu = { };
45652         if (!this.disable.cleanStyles) {
45653             cmenu = {
45654                 cls: 'x-btn-icon x-btn-clear',
45655                 
45656                 menu : {
45657                     items : []
45658                 }
45659             };
45660             for (var i =0; i < this.cleanStyles.length; i++) {
45661                 cmenu.menu.items.push({
45662                     actiontype : this.cleanStyles[i],
45663                     html: 'Remove ' + this.cleanStyles[i],
45664                     handler: function(a,b) {
45665 //                        Roo.log(a);
45666 //                        Roo.log(b);
45667                         var c = Roo.get(editorcore.doc.body);
45668                         c.select('[style]').each(function(s) {
45669                             s.dom.style.removeProperty(a.actiontype);
45670                         });
45671                         editorcore.syncValue();
45672                     },
45673                     tabIndex:-1
45674                 });
45675             }
45676              cmenu.menu.items.push({
45677                 actiontype : 'tablewidths',
45678                 html: 'Remove Table Widths',
45679                 handler: function(a,b) {
45680                     editorcore.cleanTableWidths();
45681                     editorcore.syncValue();
45682                 },
45683                 tabIndex:-1
45684             });
45685             cmenu.menu.items.push({
45686                 actiontype : 'word',
45687                 html: 'Remove MS Word Formating',
45688                 handler: function(a,b) {
45689                     editorcore.cleanWord();
45690                     editorcore.syncValue();
45691                 },
45692                 tabIndex:-1
45693             });
45694             
45695             cmenu.menu.items.push({
45696                 actiontype : 'all',
45697                 html: 'Remove All Styles',
45698                 handler: function(a,b) {
45699                     
45700                     var c = Roo.get(editorcore.doc.body);
45701                     c.select('[style]').each(function(s) {
45702                         s.dom.removeAttribute('style');
45703                     });
45704                     editorcore.syncValue();
45705                 },
45706                 tabIndex:-1
45707             });
45708             
45709             cmenu.menu.items.push({
45710                 actiontype : 'all',
45711                 html: 'Remove All CSS Classes',
45712                 handler: function(a,b) {
45713                     
45714                     var c = Roo.get(editorcore.doc.body);
45715                     c.select('[class]').each(function(s) {
45716                         s.dom.removeAttribute('class');
45717                     });
45718                     editorcore.cleanWord();
45719                     editorcore.syncValue();
45720                 },
45721                 tabIndex:-1
45722             });
45723             
45724              cmenu.menu.items.push({
45725                 actiontype : 'tidy',
45726                 html: 'Tidy HTML Source',
45727                 handler: function(a,b) {
45728                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45729                     editorcore.syncValue();
45730                 },
45731                 tabIndex:-1
45732             });
45733             
45734             
45735             tb.add(cmenu);
45736         }
45737          
45738         if (!this.disable.specialElements) {
45739             var semenu = {
45740                 text: "Other;",
45741                 cls: 'x-edit-none',
45742                 menu : {
45743                     items : []
45744                 }
45745             };
45746             for (var i =0; i < this.specialElements.length; i++) {
45747                 semenu.menu.items.push(
45748                     Roo.apply({ 
45749                         handler: function(a,b) {
45750                             editor.insertAtCursor(this.ihtml);
45751                         }
45752                     }, this.specialElements[i])
45753                 );
45754                     
45755             }
45756             
45757             tb.add(semenu);
45758             
45759             
45760         }
45761          
45762         
45763         if (this.btns) {
45764             for(var i =0; i< this.btns.length;i++) {
45765                 var b = Roo.factory(this.btns[i],Roo.form);
45766                 b.cls =  'x-edit-none';
45767                 
45768                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45769                     b.cls += ' x-init-enable';
45770                 }
45771                 
45772                 b.scope = editorcore;
45773                 tb.add(b);
45774             }
45775         
45776         }
45777         
45778         
45779         
45780         // disable everything...
45781         
45782         this.tb.items.each(function(item){
45783             
45784            if(
45785                 item.id != editorcore.frameId+ '-sourceedit' && 
45786                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45787             ){
45788                 
45789                 item.disable();
45790             }
45791         });
45792         this.rendered = true;
45793         
45794         // the all the btns;
45795         editor.on('editorevent', this.updateToolbar, this);
45796         // other toolbars need to implement this..
45797         //editor.on('editmodechange', this.updateToolbar, this);
45798     },
45799     
45800     
45801     relayBtnCmd : function(btn) {
45802         this.editorcore.relayCmd(btn.cmd);
45803     },
45804     // private used internally
45805     createLink : function(){
45806         Roo.log("create link?");
45807         var url = prompt(this.createLinkText, this.defaultLinkValue);
45808         if(url && url != 'http:/'+'/'){
45809             this.editorcore.relayCmd('createlink', url);
45810         }
45811     },
45812
45813     
45814     /**
45815      * Protected method that will not generally be called directly. It triggers
45816      * a toolbar update by reading the markup state of the current selection in the editor.
45817      */
45818     updateToolbar: function(){
45819
45820         if(!this.editorcore.activated){
45821             this.editor.onFirstFocus();
45822             return;
45823         }
45824
45825         var btns = this.tb.items.map, 
45826             doc = this.editorcore.doc,
45827             frameId = this.editorcore.frameId;
45828
45829         if(!this.disable.font && !Roo.isSafari){
45830             /*
45831             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45832             if(name != this.fontSelect.dom.value){
45833                 this.fontSelect.dom.value = name;
45834             }
45835             */
45836         }
45837         if(!this.disable.format){
45838             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45839             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45840             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45841             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45842         }
45843         if(!this.disable.alignments){
45844             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45845             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45846             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45847         }
45848         if(!Roo.isSafari && !this.disable.lists){
45849             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45850             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45851         }
45852         
45853         var ans = this.editorcore.getAllAncestors();
45854         if (this.formatCombo) {
45855             
45856             
45857             var store = this.formatCombo.store;
45858             this.formatCombo.setValue("");
45859             for (var i =0; i < ans.length;i++) {
45860                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45861                     // select it..
45862                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45863                     break;
45864                 }
45865             }
45866         }
45867         
45868         
45869         
45870         // hides menus... - so this cant be on a menu...
45871         Roo.menu.MenuMgr.hideAll();
45872
45873         //this.editorsyncValue();
45874     },
45875    
45876     
45877     createFontOptions : function(){
45878         var buf = [], fs = this.fontFamilies, ff, lc;
45879         
45880         
45881         
45882         for(var i = 0, len = fs.length; i< len; i++){
45883             ff = fs[i];
45884             lc = ff.toLowerCase();
45885             buf.push(
45886                 '<option value="',lc,'" style="font-family:',ff,';"',
45887                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45888                     ff,
45889                 '</option>'
45890             );
45891         }
45892         return buf.join('');
45893     },
45894     
45895     toggleSourceEdit : function(sourceEditMode){
45896         
45897         Roo.log("toolbar toogle");
45898         if(sourceEditMode === undefined){
45899             sourceEditMode = !this.sourceEditMode;
45900         }
45901         this.sourceEditMode = sourceEditMode === true;
45902         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45903         // just toggle the button?
45904         if(btn.pressed !== this.sourceEditMode){
45905             btn.toggle(this.sourceEditMode);
45906             return;
45907         }
45908         
45909         if(sourceEditMode){
45910             Roo.log("disabling buttons");
45911             this.tb.items.each(function(item){
45912                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45913                     item.disable();
45914                 }
45915             });
45916           
45917         }else{
45918             Roo.log("enabling buttons");
45919             if(this.editorcore.initialized){
45920                 this.tb.items.each(function(item){
45921                     item.enable();
45922                 });
45923             }
45924             
45925         }
45926         Roo.log("calling toggole on editor");
45927         // tell the editor that it's been pressed..
45928         this.editor.toggleSourceEdit(sourceEditMode);
45929        
45930     },
45931      /**
45932      * Object collection of toolbar tooltips for the buttons in the editor. The key
45933      * is the command id associated with that button and the value is a valid QuickTips object.
45934      * For example:
45935 <pre><code>
45936 {
45937     bold : {
45938         title: 'Bold (Ctrl+B)',
45939         text: 'Make the selected text bold.',
45940         cls: 'x-html-editor-tip'
45941     },
45942     italic : {
45943         title: 'Italic (Ctrl+I)',
45944         text: 'Make the selected text italic.',
45945         cls: 'x-html-editor-tip'
45946     },
45947     ...
45948 </code></pre>
45949     * @type Object
45950      */
45951     buttonTips : {
45952         bold : {
45953             title: 'Bold (Ctrl+B)',
45954             text: 'Make the selected text bold.',
45955             cls: 'x-html-editor-tip'
45956         },
45957         italic : {
45958             title: 'Italic (Ctrl+I)',
45959             text: 'Make the selected text italic.',
45960             cls: 'x-html-editor-tip'
45961         },
45962         underline : {
45963             title: 'Underline (Ctrl+U)',
45964             text: 'Underline the selected text.',
45965             cls: 'x-html-editor-tip'
45966         },
45967         strikethrough : {
45968             title: 'Strikethrough',
45969             text: 'Strikethrough the selected text.',
45970             cls: 'x-html-editor-tip'
45971         },
45972         increasefontsize : {
45973             title: 'Grow Text',
45974             text: 'Increase the font size.',
45975             cls: 'x-html-editor-tip'
45976         },
45977         decreasefontsize : {
45978             title: 'Shrink Text',
45979             text: 'Decrease the font size.',
45980             cls: 'x-html-editor-tip'
45981         },
45982         backcolor : {
45983             title: 'Text Highlight Color',
45984             text: 'Change the background color of the selected text.',
45985             cls: 'x-html-editor-tip'
45986         },
45987         forecolor : {
45988             title: 'Font Color',
45989             text: 'Change the color of the selected text.',
45990             cls: 'x-html-editor-tip'
45991         },
45992         justifyleft : {
45993             title: 'Align Text Left',
45994             text: 'Align text to the left.',
45995             cls: 'x-html-editor-tip'
45996         },
45997         justifycenter : {
45998             title: 'Center Text',
45999             text: 'Center text in the editor.',
46000             cls: 'x-html-editor-tip'
46001         },
46002         justifyright : {
46003             title: 'Align Text Right',
46004             text: 'Align text to the right.',
46005             cls: 'x-html-editor-tip'
46006         },
46007         insertunorderedlist : {
46008             title: 'Bullet List',
46009             text: 'Start a bulleted list.',
46010             cls: 'x-html-editor-tip'
46011         },
46012         insertorderedlist : {
46013             title: 'Numbered List',
46014             text: 'Start a numbered list.',
46015             cls: 'x-html-editor-tip'
46016         },
46017         createlink : {
46018             title: 'Hyperlink',
46019             text: 'Make the selected text a hyperlink.',
46020             cls: 'x-html-editor-tip'
46021         },
46022         sourceedit : {
46023             title: 'Source Edit',
46024             text: 'Switch to source editing mode.',
46025             cls: 'x-html-editor-tip'
46026         }
46027     },
46028     // private
46029     onDestroy : function(){
46030         if(this.rendered){
46031             
46032             this.tb.items.each(function(item){
46033                 if(item.menu){
46034                     item.menu.removeAll();
46035                     if(item.menu.el){
46036                         item.menu.el.destroy();
46037                     }
46038                 }
46039                 item.destroy();
46040             });
46041              
46042         }
46043     },
46044     onFirstFocus: function() {
46045         this.tb.items.each(function(item){
46046            item.enable();
46047         });
46048     }
46049 });
46050
46051
46052
46053
46054 // <script type="text/javascript">
46055 /*
46056  * Based on
46057  * Ext JS Library 1.1.1
46058  * Copyright(c) 2006-2007, Ext JS, LLC.
46059  *  
46060  
46061  */
46062
46063  
46064 /**
46065  * @class Roo.form.HtmlEditor.ToolbarContext
46066  * Context Toolbar
46067  * 
46068  * Usage:
46069  *
46070  new Roo.form.HtmlEditor({
46071     ....
46072     toolbars : [
46073         { xtype: 'ToolbarStandard', styles : {} }
46074         { xtype: 'ToolbarContext', disable : {} }
46075     ]
46076 })
46077
46078      
46079  * 
46080  * @config : {Object} disable List of elements to disable.. (not done yet.)
46081  * @config : {Object} styles  Map of styles available.
46082  * 
46083  */
46084
46085 Roo.form.HtmlEditor.ToolbarContext = function(config)
46086 {
46087     
46088     Roo.apply(this, config);
46089     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46090     // dont call parent... till later.
46091     this.styles = this.styles || {};
46092 }
46093
46094  
46095
46096 Roo.form.HtmlEditor.ToolbarContext.types = {
46097     'IMG' : {
46098         width : {
46099             title: "Width",
46100             width: 40
46101         },
46102         height:  {
46103             title: "Height",
46104             width: 40
46105         },
46106         align: {
46107             title: "Align",
46108             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46109             width : 80
46110             
46111         },
46112         border: {
46113             title: "Border",
46114             width: 40
46115         },
46116         alt: {
46117             title: "Alt",
46118             width: 120
46119         },
46120         src : {
46121             title: "Src",
46122             width: 220
46123         }
46124         
46125     },
46126     'A' : {
46127         name : {
46128             title: "Name",
46129             width: 50
46130         },
46131         target:  {
46132             title: "Target",
46133             width: 120
46134         },
46135         href:  {
46136             title: "Href",
46137             width: 220
46138         } // border?
46139         
46140     },
46141     'TABLE' : {
46142         rows : {
46143             title: "Rows",
46144             width: 20
46145         },
46146         cols : {
46147             title: "Cols",
46148             width: 20
46149         },
46150         width : {
46151             title: "Width",
46152             width: 40
46153         },
46154         height : {
46155             title: "Height",
46156             width: 40
46157         },
46158         border : {
46159             title: "Border",
46160             width: 20
46161         }
46162     },
46163     'TD' : {
46164         width : {
46165             title: "Width",
46166             width: 40
46167         },
46168         height : {
46169             title: "Height",
46170             width: 40
46171         },   
46172         align: {
46173             title: "Align",
46174             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46175             width: 80
46176         },
46177         valign: {
46178             title: "Valign",
46179             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46180             width: 80
46181         },
46182         colspan: {
46183             title: "Colspan",
46184             width: 20
46185             
46186         },
46187          'font-family'  : {
46188             title : "Font",
46189             style : 'fontFamily',
46190             displayField: 'display',
46191             optname : 'font-family',
46192             width: 140
46193         }
46194     },
46195     'INPUT' : {
46196         name : {
46197             title: "name",
46198             width: 120
46199         },
46200         value : {
46201             title: "Value",
46202             width: 120
46203         },
46204         width : {
46205             title: "Width",
46206             width: 40
46207         }
46208     },
46209     'LABEL' : {
46210         'for' : {
46211             title: "For",
46212             width: 120
46213         }
46214     },
46215     'TEXTAREA' : {
46216           name : {
46217             title: "name",
46218             width: 120
46219         },
46220         rows : {
46221             title: "Rows",
46222             width: 20
46223         },
46224         cols : {
46225             title: "Cols",
46226             width: 20
46227         }
46228     },
46229     'SELECT' : {
46230         name : {
46231             title: "name",
46232             width: 120
46233         },
46234         selectoptions : {
46235             title: "Options",
46236             width: 200
46237         }
46238     },
46239     
46240     // should we really allow this??
46241     // should this just be 
46242     'BODY' : {
46243         title : {
46244             title: "Title",
46245             width: 200,
46246             disabled : true
46247         }
46248     },
46249     'SPAN' : {
46250         'font-family'  : {
46251             title : "Font",
46252             style : 'fontFamily',
46253             displayField: 'display',
46254             optname : 'font-family',
46255             width: 140
46256         }
46257     },
46258     'DIV' : {
46259         'font-family'  : {
46260             title : "Font",
46261             style : 'fontFamily',
46262             displayField: 'display',
46263             optname : 'font-family',
46264             width: 140
46265         }
46266     },
46267      'P' : {
46268         'font-family'  : {
46269             title : "Font",
46270             style : 'fontFamily',
46271             displayField: 'display',
46272             optname : 'font-family',
46273             width: 140
46274         }
46275     },
46276     
46277     '*' : {
46278         // empty..
46279     }
46280
46281 };
46282
46283 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46284 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46285
46286 Roo.form.HtmlEditor.ToolbarContext.options = {
46287         'font-family'  : [ 
46288                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46289                 [ 'Courier New', 'Courier New'],
46290                 [ 'Tahoma', 'Tahoma'],
46291                 [ 'Times New Roman,serif', 'Times'],
46292                 [ 'Verdana','Verdana' ]
46293         ]
46294 };
46295
46296 // fixme - these need to be configurable..
46297  
46298
46299 //Roo.form.HtmlEditor.ToolbarContext.types
46300
46301
46302 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46303     
46304     tb: false,
46305     
46306     rendered: false,
46307     
46308     editor : false,
46309     editorcore : false,
46310     /**
46311      * @cfg {Object} disable  List of toolbar elements to disable
46312          
46313      */
46314     disable : false,
46315     /**
46316      * @cfg {Object} styles List of styles 
46317      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46318      *
46319      * These must be defined in the page, so they get rendered correctly..
46320      * .headline { }
46321      * TD.underline { }
46322      * 
46323      */
46324     styles : false,
46325     
46326     options: false,
46327     
46328     toolbars : false,
46329     
46330     init : function(editor)
46331     {
46332         this.editor = editor;
46333         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46334         var editorcore = this.editorcore;
46335         
46336         var fid = editorcore.frameId;
46337         var etb = this;
46338         function btn(id, toggle, handler){
46339             var xid = fid + '-'+ id ;
46340             return {
46341                 id : xid,
46342                 cmd : id,
46343                 cls : 'x-btn-icon x-edit-'+id,
46344                 enableToggle:toggle !== false,
46345                 scope: editorcore, // was editor...
46346                 handler:handler||editorcore.relayBtnCmd,
46347                 clickEvent:'mousedown',
46348                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46349                 tabIndex:-1
46350             };
46351         }
46352         // create a new element.
46353         var wdiv = editor.wrap.createChild({
46354                 tag: 'div'
46355             }, editor.wrap.dom.firstChild.nextSibling, true);
46356         
46357         // can we do this more than once??
46358         
46359          // stop form submits
46360       
46361  
46362         // disable everything...
46363         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46364         this.toolbars = {};
46365            
46366         for (var i in  ty) {
46367           
46368             this.toolbars[i] = this.buildToolbar(ty[i],i);
46369         }
46370         this.tb = this.toolbars.BODY;
46371         this.tb.el.show();
46372         this.buildFooter();
46373         this.footer.show();
46374         editor.on('hide', function( ) { this.footer.hide() }, this);
46375         editor.on('show', function( ) { this.footer.show() }, this);
46376         
46377          
46378         this.rendered = true;
46379         
46380         // the all the btns;
46381         editor.on('editorevent', this.updateToolbar, this);
46382         // other toolbars need to implement this..
46383         //editor.on('editmodechange', this.updateToolbar, this);
46384     },
46385     
46386     
46387     
46388     /**
46389      * Protected method that will not generally be called directly. It triggers
46390      * a toolbar update by reading the markup state of the current selection in the editor.
46391      *
46392      * Note you can force an update by calling on('editorevent', scope, false)
46393      */
46394     updateToolbar: function(editor,ev,sel){
46395
46396         //Roo.log(ev);
46397         // capture mouse up - this is handy for selecting images..
46398         // perhaps should go somewhere else...
46399         if(!this.editorcore.activated){
46400              this.editor.onFirstFocus();
46401             return;
46402         }
46403         
46404         
46405         
46406         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46407         // selectNode - might want to handle IE?
46408         if (ev &&
46409             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46410             ev.target && ev.target.tagName == 'IMG') {
46411             // they have click on an image...
46412             // let's see if we can change the selection...
46413             sel = ev.target;
46414          
46415               var nodeRange = sel.ownerDocument.createRange();
46416             try {
46417                 nodeRange.selectNode(sel);
46418             } catch (e) {
46419                 nodeRange.selectNodeContents(sel);
46420             }
46421             //nodeRange.collapse(true);
46422             var s = this.editorcore.win.getSelection();
46423             s.removeAllRanges();
46424             s.addRange(nodeRange);
46425         }  
46426         
46427       
46428         var updateFooter = sel ? false : true;
46429         
46430         
46431         var ans = this.editorcore.getAllAncestors();
46432         
46433         // pick
46434         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46435         
46436         if (!sel) { 
46437             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46438             sel = sel ? sel : this.editorcore.doc.body;
46439             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46440             
46441         }
46442         // pick a menu that exists..
46443         var tn = sel.tagName.toUpperCase();
46444         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46445         
46446         tn = sel.tagName.toUpperCase();
46447         
46448         var lastSel = this.tb.selectedNode;
46449         
46450         this.tb.selectedNode = sel;
46451         
46452         // if current menu does not match..
46453         
46454         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46455                 
46456             this.tb.el.hide();
46457             ///console.log("show: " + tn);
46458             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46459             this.tb.el.show();
46460             // update name
46461             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46462             
46463             
46464             // update attributes
46465             if (this.tb.fields) {
46466                 this.tb.fields.each(function(e) {
46467                     if (e.stylename) {
46468                         e.setValue(sel.style[e.stylename]);
46469                         return;
46470                     } 
46471                    e.setValue(sel.getAttribute(e.attrname));
46472                 });
46473             }
46474             
46475             var hasStyles = false;
46476             for(var i in this.styles) {
46477                 hasStyles = true;
46478                 break;
46479             }
46480             
46481             // update styles
46482             if (hasStyles) { 
46483                 var st = this.tb.fields.item(0);
46484                 
46485                 st.store.removeAll();
46486                
46487                 
46488                 var cn = sel.className.split(/\s+/);
46489                 
46490                 var avs = [];
46491                 if (this.styles['*']) {
46492                     
46493                     Roo.each(this.styles['*'], function(v) {
46494                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46495                     });
46496                 }
46497                 if (this.styles[tn]) { 
46498                     Roo.each(this.styles[tn], function(v) {
46499                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46500                     });
46501                 }
46502                 
46503                 st.store.loadData(avs);
46504                 st.collapse();
46505                 st.setValue(cn);
46506             }
46507             // flag our selected Node.
46508             this.tb.selectedNode = sel;
46509            
46510            
46511             Roo.menu.MenuMgr.hideAll();
46512
46513         }
46514         
46515         if (!updateFooter) {
46516             //this.footDisp.dom.innerHTML = ''; 
46517             return;
46518         }
46519         // update the footer
46520         //
46521         var html = '';
46522         
46523         this.footerEls = ans.reverse();
46524         Roo.each(this.footerEls, function(a,i) {
46525             if (!a) { return; }
46526             html += html.length ? ' &gt; '  :  '';
46527             
46528             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46529             
46530         });
46531        
46532         // 
46533         var sz = this.footDisp.up('td').getSize();
46534         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46535         this.footDisp.dom.style.marginLeft = '5px';
46536         
46537         this.footDisp.dom.style.overflow = 'hidden';
46538         
46539         this.footDisp.dom.innerHTML = html;
46540             
46541         //this.editorsyncValue();
46542     },
46543      
46544     
46545    
46546        
46547     // private
46548     onDestroy : function(){
46549         if(this.rendered){
46550             
46551             this.tb.items.each(function(item){
46552                 if(item.menu){
46553                     item.menu.removeAll();
46554                     if(item.menu.el){
46555                         item.menu.el.destroy();
46556                     }
46557                 }
46558                 item.destroy();
46559             });
46560              
46561         }
46562     },
46563     onFirstFocus: function() {
46564         // need to do this for all the toolbars..
46565         this.tb.items.each(function(item){
46566            item.enable();
46567         });
46568     },
46569     buildToolbar: function(tlist, nm)
46570     {
46571         var editor = this.editor;
46572         var editorcore = this.editorcore;
46573          // create a new element.
46574         var wdiv = editor.wrap.createChild({
46575                 tag: 'div'
46576             }, editor.wrap.dom.firstChild.nextSibling, true);
46577         
46578        
46579         var tb = new Roo.Toolbar(wdiv);
46580         // add the name..
46581         
46582         tb.add(nm+ ":&nbsp;");
46583         
46584         var styles = [];
46585         for(var i in this.styles) {
46586             styles.push(i);
46587         }
46588         
46589         // styles...
46590         if (styles && styles.length) {
46591             
46592             // this needs a multi-select checkbox...
46593             tb.addField( new Roo.form.ComboBox({
46594                 store: new Roo.data.SimpleStore({
46595                     id : 'val',
46596                     fields: ['val', 'selected'],
46597                     data : [] 
46598                 }),
46599                 name : '-roo-edit-className',
46600                 attrname : 'className',
46601                 displayField: 'val',
46602                 typeAhead: false,
46603                 mode: 'local',
46604                 editable : false,
46605                 triggerAction: 'all',
46606                 emptyText:'Select Style',
46607                 selectOnFocus:true,
46608                 width: 130,
46609                 listeners : {
46610                     'select': function(c, r, i) {
46611                         // initial support only for on class per el..
46612                         tb.selectedNode.className =  r ? r.get('val') : '';
46613                         editorcore.syncValue();
46614                     }
46615                 }
46616     
46617             }));
46618         }
46619         
46620         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46621         var tbops = tbc.options;
46622         
46623         for (var i in tlist) {
46624             
46625             var item = tlist[i];
46626             tb.add(item.title + ":&nbsp;");
46627             
46628             
46629             //optname == used so you can configure the options available..
46630             var opts = item.opts ? item.opts : false;
46631             if (item.optname) {
46632                 opts = tbops[item.optname];
46633            
46634             }
46635             
46636             if (opts) {
46637                 // opts == pulldown..
46638                 tb.addField( new Roo.form.ComboBox({
46639                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46640                         id : 'val',
46641                         fields: ['val', 'display'],
46642                         data : opts  
46643                     }),
46644                     name : '-roo-edit-' + i,
46645                     attrname : i,
46646                     stylename : item.style ? item.style : false,
46647                     displayField: item.displayField ? item.displayField : 'val',
46648                     valueField :  'val',
46649                     typeAhead: false,
46650                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46651                     editable : false,
46652                     triggerAction: 'all',
46653                     emptyText:'Select',
46654                     selectOnFocus:true,
46655                     width: item.width ? item.width  : 130,
46656                     listeners : {
46657                         'select': function(c, r, i) {
46658                             if (c.stylename) {
46659                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46660                                 return;
46661                             }
46662                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46663                         }
46664                     }
46665
46666                 }));
46667                 continue;
46668                     
46669                  
46670                 
46671                 tb.addField( new Roo.form.TextField({
46672                     name: i,
46673                     width: 100,
46674                     //allowBlank:false,
46675                     value: ''
46676                 }));
46677                 continue;
46678             }
46679             tb.addField( new Roo.form.TextField({
46680                 name: '-roo-edit-' + i,
46681                 attrname : i,
46682                 
46683                 width: item.width,
46684                 //allowBlank:true,
46685                 value: '',
46686                 listeners: {
46687                     'change' : function(f, nv, ov) {
46688                         tb.selectedNode.setAttribute(f.attrname, nv);
46689                         editorcore.syncValue();
46690                     }
46691                 }
46692             }));
46693              
46694         }
46695         
46696         var _this = this;
46697         
46698         if(nm == 'BODY'){
46699             tb.addSeparator();
46700         
46701             tb.addButton( {
46702                 text: 'Stylesheets',
46703
46704                 listeners : {
46705                     click : function ()
46706                     {
46707                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46708                     }
46709                 }
46710             });
46711         }
46712         
46713         tb.addFill();
46714         tb.addButton( {
46715             text: 'Remove Tag',
46716     
46717             listeners : {
46718                 click : function ()
46719                 {
46720                     // remove
46721                     // undo does not work.
46722                      
46723                     var sn = tb.selectedNode;
46724                     
46725                     var pn = sn.parentNode;
46726                     
46727                     var stn =  sn.childNodes[0];
46728                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46729                     while (sn.childNodes.length) {
46730                         var node = sn.childNodes[0];
46731                         sn.removeChild(node);
46732                         //Roo.log(node);
46733                         pn.insertBefore(node, sn);
46734                         
46735                     }
46736                     pn.removeChild(sn);
46737                     var range = editorcore.createRange();
46738         
46739                     range.setStart(stn,0);
46740                     range.setEnd(en,0); //????
46741                     //range.selectNode(sel);
46742                     
46743                     
46744                     var selection = editorcore.getSelection();
46745                     selection.removeAllRanges();
46746                     selection.addRange(range);
46747                     
46748                     
46749                     
46750                     //_this.updateToolbar(null, null, pn);
46751                     _this.updateToolbar(null, null, null);
46752                     _this.footDisp.dom.innerHTML = ''; 
46753                 }
46754             }
46755             
46756                     
46757                 
46758             
46759         });
46760         
46761         
46762         tb.el.on('click', function(e){
46763             e.preventDefault(); // what does this do?
46764         });
46765         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46766         tb.el.hide();
46767         tb.name = nm;
46768         // dont need to disable them... as they will get hidden
46769         return tb;
46770          
46771         
46772     },
46773     buildFooter : function()
46774     {
46775         
46776         var fel = this.editor.wrap.createChild();
46777         this.footer = new Roo.Toolbar(fel);
46778         // toolbar has scrolly on left / right?
46779         var footDisp= new Roo.Toolbar.Fill();
46780         var _t = this;
46781         this.footer.add(
46782             {
46783                 text : '&lt;',
46784                 xtype: 'Button',
46785                 handler : function() {
46786                     _t.footDisp.scrollTo('left',0,true)
46787                 }
46788             }
46789         );
46790         this.footer.add( footDisp );
46791         this.footer.add( 
46792             {
46793                 text : '&gt;',
46794                 xtype: 'Button',
46795                 handler : function() {
46796                     // no animation..
46797                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46798                 }
46799             }
46800         );
46801         var fel = Roo.get(footDisp.el);
46802         fel.addClass('x-editor-context');
46803         this.footDispWrap = fel; 
46804         this.footDispWrap.overflow  = 'hidden';
46805         
46806         this.footDisp = fel.createChild();
46807         this.footDispWrap.on('click', this.onContextClick, this)
46808         
46809         
46810     },
46811     onContextClick : function (ev,dom)
46812     {
46813         ev.preventDefault();
46814         var  cn = dom.className;
46815         //Roo.log(cn);
46816         if (!cn.match(/x-ed-loc-/)) {
46817             return;
46818         }
46819         var n = cn.split('-').pop();
46820         var ans = this.footerEls;
46821         var sel = ans[n];
46822         
46823          // pick
46824         var range = this.editorcore.createRange();
46825         
46826         range.selectNodeContents(sel);
46827         //range.selectNode(sel);
46828         
46829         
46830         var selection = this.editorcore.getSelection();
46831         selection.removeAllRanges();
46832         selection.addRange(range);
46833         
46834         
46835         
46836         this.updateToolbar(null, null, sel);
46837         
46838         
46839     }
46840     
46841     
46842     
46843     
46844     
46845 });
46846
46847
46848
46849
46850
46851 /*
46852  * Based on:
46853  * Ext JS Library 1.1.1
46854  * Copyright(c) 2006-2007, Ext JS, LLC.
46855  *
46856  * Originally Released Under LGPL - original licence link has changed is not relivant.
46857  *
46858  * Fork - LGPL
46859  * <script type="text/javascript">
46860  */
46861  
46862 /**
46863  * @class Roo.form.BasicForm
46864  * @extends Roo.util.Observable
46865  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46866  * @constructor
46867  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46868  * @param {Object} config Configuration options
46869  */
46870 Roo.form.BasicForm = function(el, config){
46871     this.allItems = [];
46872     this.childForms = [];
46873     Roo.apply(this, config);
46874     /*
46875      * The Roo.form.Field items in this form.
46876      * @type MixedCollection
46877      */
46878      
46879      
46880     this.items = new Roo.util.MixedCollection(false, function(o){
46881         return o.id || (o.id = Roo.id());
46882     });
46883     this.addEvents({
46884         /**
46885          * @event beforeaction
46886          * Fires before any action is performed. Return false to cancel the action.
46887          * @param {Form} this
46888          * @param {Action} action The action to be performed
46889          */
46890         beforeaction: true,
46891         /**
46892          * @event actionfailed
46893          * Fires when an action fails.
46894          * @param {Form} this
46895          * @param {Action} action The action that failed
46896          */
46897         actionfailed : true,
46898         /**
46899          * @event actioncomplete
46900          * Fires when an action is completed.
46901          * @param {Form} this
46902          * @param {Action} action The action that completed
46903          */
46904         actioncomplete : true
46905     });
46906     if(el){
46907         this.initEl(el);
46908     }
46909     Roo.form.BasicForm.superclass.constructor.call(this);
46910     
46911     Roo.form.BasicForm.popover.apply();
46912 };
46913
46914 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46915     /**
46916      * @cfg {String} method
46917      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46918      */
46919     /**
46920      * @cfg {DataReader} reader
46921      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46922      * This is optional as there is built-in support for processing JSON.
46923      */
46924     /**
46925      * @cfg {DataReader} errorReader
46926      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46927      * This is completely optional as there is built-in support for processing JSON.
46928      */
46929     /**
46930      * @cfg {String} url
46931      * The URL to use for form actions if one isn't supplied in the action options.
46932      */
46933     /**
46934      * @cfg {Boolean} fileUpload
46935      * Set to true if this form is a file upload.
46936      */
46937      
46938     /**
46939      * @cfg {Object} baseParams
46940      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46941      */
46942      /**
46943      
46944     /**
46945      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46946      */
46947     timeout: 30,
46948
46949     // private
46950     activeAction : null,
46951
46952     /**
46953      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46954      * or setValues() data instead of when the form was first created.
46955      */
46956     trackResetOnLoad : false,
46957     
46958     
46959     /**
46960      * childForms - used for multi-tab forms
46961      * @type {Array}
46962      */
46963     childForms : false,
46964     
46965     /**
46966      * allItems - full list of fields.
46967      * @type {Array}
46968      */
46969     allItems : false,
46970     
46971     /**
46972      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46973      * element by passing it or its id or mask the form itself by passing in true.
46974      * @type Mixed
46975      */
46976     waitMsgTarget : false,
46977     
46978     /**
46979      * @type Boolean
46980      */
46981     disableMask : false,
46982     
46983     /**
46984      * @cfg {Boolean} errorMask (true|false) default false
46985      */
46986     errorMask : false,
46987     
46988     /**
46989      * @cfg {Number} maskOffset Default 100
46990      */
46991     maskOffset : 100,
46992
46993     // private
46994     initEl : function(el){
46995         this.el = Roo.get(el);
46996         this.id = this.el.id || Roo.id();
46997         this.el.on('submit', this.onSubmit, this);
46998         this.el.addClass('x-form');
46999     },
47000
47001     // private
47002     onSubmit : function(e){
47003         e.stopEvent();
47004     },
47005
47006     /**
47007      * Returns true if client-side validation on the form is successful.
47008      * @return Boolean
47009      */
47010     isValid : function(){
47011         var valid = true;
47012         var target = false;
47013         this.items.each(function(f){
47014             if(f.validate()){
47015                 return;
47016             }
47017             
47018             valid = false;
47019                 
47020             if(!target && f.el.isVisible(true)){
47021                 target = f;
47022             }
47023         });
47024         
47025         if(this.errorMask && !valid){
47026             Roo.form.BasicForm.popover.mask(this, target);
47027         }
47028         
47029         return valid;
47030     },
47031
47032     /**
47033      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47034      * @return Boolean
47035      */
47036     isDirty : function(){
47037         var dirty = false;
47038         this.items.each(function(f){
47039            if(f.isDirty()){
47040                dirty = true;
47041                return false;
47042            }
47043         });
47044         return dirty;
47045     },
47046     
47047     /**
47048      * Returns true if any fields in this form have changed since their original load. (New version)
47049      * @return Boolean
47050      */
47051     
47052     hasChanged : function()
47053     {
47054         var dirty = false;
47055         this.items.each(function(f){
47056            if(f.hasChanged()){
47057                dirty = true;
47058                return false;
47059            }
47060         });
47061         return dirty;
47062         
47063     },
47064     /**
47065      * Resets all hasChanged to 'false' -
47066      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47067      * So hasChanged storage is only to be used for this purpose
47068      * @return Boolean
47069      */
47070     resetHasChanged : function()
47071     {
47072         this.items.each(function(f){
47073            f.resetHasChanged();
47074         });
47075         
47076     },
47077     
47078     
47079     /**
47080      * Performs a predefined action (submit or load) or custom actions you define on this form.
47081      * @param {String} actionName The name of the action type
47082      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47083      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47084      * accept other config options):
47085      * <pre>
47086 Property          Type             Description
47087 ----------------  ---------------  ----------------------------------------------------------------------------------
47088 url               String           The url for the action (defaults to the form's url)
47089 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47090 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47091 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47092                                    validate the form on the client (defaults to false)
47093      * </pre>
47094      * @return {BasicForm} this
47095      */
47096     doAction : function(action, options){
47097         if(typeof action == 'string'){
47098             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47099         }
47100         if(this.fireEvent('beforeaction', this, action) !== false){
47101             this.beforeAction(action);
47102             action.run.defer(100, action);
47103         }
47104         return this;
47105     },
47106
47107     /**
47108      * Shortcut to do a submit action.
47109      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47110      * @return {BasicForm} this
47111      */
47112     submit : function(options){
47113         this.doAction('submit', options);
47114         return this;
47115     },
47116
47117     /**
47118      * Shortcut to do a load action.
47119      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47120      * @return {BasicForm} this
47121      */
47122     load : function(options){
47123         this.doAction('load', options);
47124         return this;
47125     },
47126
47127     /**
47128      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47129      * @param {Record} record The record to edit
47130      * @return {BasicForm} this
47131      */
47132     updateRecord : function(record){
47133         record.beginEdit();
47134         var fs = record.fields;
47135         fs.each(function(f){
47136             var field = this.findField(f.name);
47137             if(field){
47138                 record.set(f.name, field.getValue());
47139             }
47140         }, this);
47141         record.endEdit();
47142         return this;
47143     },
47144
47145     /**
47146      * Loads an Roo.data.Record into this form.
47147      * @param {Record} record The record to load
47148      * @return {BasicForm} this
47149      */
47150     loadRecord : function(record){
47151         this.setValues(record.data);
47152         return this;
47153     },
47154
47155     // private
47156     beforeAction : function(action){
47157         var o = action.options;
47158         
47159         if(!this.disableMask) {
47160             if(this.waitMsgTarget === true){
47161                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47162             }else if(this.waitMsgTarget){
47163                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47164                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47165             }else {
47166                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47167             }
47168         }
47169         
47170          
47171     },
47172
47173     // private
47174     afterAction : function(action, success){
47175         this.activeAction = null;
47176         var o = action.options;
47177         
47178         if(!this.disableMask) {
47179             if(this.waitMsgTarget === true){
47180                 this.el.unmask();
47181             }else if(this.waitMsgTarget){
47182                 this.waitMsgTarget.unmask();
47183             }else{
47184                 Roo.MessageBox.updateProgress(1);
47185                 Roo.MessageBox.hide();
47186             }
47187         }
47188         
47189         if(success){
47190             if(o.reset){
47191                 this.reset();
47192             }
47193             Roo.callback(o.success, o.scope, [this, action]);
47194             this.fireEvent('actioncomplete', this, action);
47195             
47196         }else{
47197             
47198             // failure condition..
47199             // we have a scenario where updates need confirming.
47200             // eg. if a locking scenario exists..
47201             // we look for { errors : { needs_confirm : true }} in the response.
47202             if (
47203                 (typeof(action.result) != 'undefined')  &&
47204                 (typeof(action.result.errors) != 'undefined')  &&
47205                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47206            ){
47207                 var _t = this;
47208                 Roo.MessageBox.confirm(
47209                     "Change requires confirmation",
47210                     action.result.errorMsg,
47211                     function(r) {
47212                         if (r != 'yes') {
47213                             return;
47214                         }
47215                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47216                     }
47217                     
47218                 );
47219                 
47220                 
47221                 
47222                 return;
47223             }
47224             
47225             Roo.callback(o.failure, o.scope, [this, action]);
47226             // show an error message if no failed handler is set..
47227             if (!this.hasListener('actionfailed')) {
47228                 Roo.MessageBox.alert("Error",
47229                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47230                         action.result.errorMsg :
47231                         "Saving Failed, please check your entries or try again"
47232                 );
47233             }
47234             
47235             this.fireEvent('actionfailed', this, action);
47236         }
47237         
47238     },
47239
47240     /**
47241      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47242      * @param {String} id The value to search for
47243      * @return Field
47244      */
47245     findField : function(id){
47246         var field = this.items.get(id);
47247         if(!field){
47248             this.items.each(function(f){
47249                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47250                     field = f;
47251                     return false;
47252                 }
47253             });
47254         }
47255         return field || null;
47256     },
47257
47258     /**
47259      * Add a secondary form to this one, 
47260      * Used to provide tabbed forms. One form is primary, with hidden values 
47261      * which mirror the elements from the other forms.
47262      * 
47263      * @param {Roo.form.Form} form to add.
47264      * 
47265      */
47266     addForm : function(form)
47267     {
47268        
47269         if (this.childForms.indexOf(form) > -1) {
47270             // already added..
47271             return;
47272         }
47273         this.childForms.push(form);
47274         var n = '';
47275         Roo.each(form.allItems, function (fe) {
47276             
47277             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47278             if (this.findField(n)) { // already added..
47279                 return;
47280             }
47281             var add = new Roo.form.Hidden({
47282                 name : n
47283             });
47284             add.render(this.el);
47285             
47286             this.add( add );
47287         }, this);
47288         
47289     },
47290     /**
47291      * Mark fields in this form invalid in bulk.
47292      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47293      * @return {BasicForm} this
47294      */
47295     markInvalid : function(errors){
47296         if(errors instanceof Array){
47297             for(var i = 0, len = errors.length; i < len; i++){
47298                 var fieldError = errors[i];
47299                 var f = this.findField(fieldError.id);
47300                 if(f){
47301                     f.markInvalid(fieldError.msg);
47302                 }
47303             }
47304         }else{
47305             var field, id;
47306             for(id in errors){
47307                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47308                     field.markInvalid(errors[id]);
47309                 }
47310             }
47311         }
47312         Roo.each(this.childForms || [], function (f) {
47313             f.markInvalid(errors);
47314         });
47315         
47316         return this;
47317     },
47318
47319     /**
47320      * Set values for fields in this form in bulk.
47321      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47322      * @return {BasicForm} this
47323      */
47324     setValues : function(values){
47325         if(values instanceof Array){ // array of objects
47326             for(var i = 0, len = values.length; i < len; i++){
47327                 var v = values[i];
47328                 var f = this.findField(v.id);
47329                 if(f){
47330                     f.setValue(v.value);
47331                     if(this.trackResetOnLoad){
47332                         f.originalValue = f.getValue();
47333                     }
47334                 }
47335             }
47336         }else{ // object hash
47337             var field, id;
47338             for(id in values){
47339                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47340                     
47341                     if (field.setFromData && 
47342                         field.valueField && 
47343                         field.displayField &&
47344                         // combos' with local stores can 
47345                         // be queried via setValue()
47346                         // to set their value..
47347                         (field.store && !field.store.isLocal)
47348                         ) {
47349                         // it's a combo
47350                         var sd = { };
47351                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47352                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47353                         field.setFromData(sd);
47354                         
47355                     } else {
47356                         field.setValue(values[id]);
47357                     }
47358                     
47359                     
47360                     if(this.trackResetOnLoad){
47361                         field.originalValue = field.getValue();
47362                     }
47363                 }
47364             }
47365         }
47366         this.resetHasChanged();
47367         
47368         
47369         Roo.each(this.childForms || [], function (f) {
47370             f.setValues(values);
47371             f.resetHasChanged();
47372         });
47373                 
47374         return this;
47375     },
47376  
47377     /**
47378      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47379      * they are returned as an array.
47380      * @param {Boolean} asString
47381      * @return {Object}
47382      */
47383     getValues : function(asString){
47384         if (this.childForms) {
47385             // copy values from the child forms
47386             Roo.each(this.childForms, function (f) {
47387                 this.setValues(f.getValues());
47388             }, this);
47389         }
47390         
47391         // use formdata
47392         if (typeof(FormData) != 'undefined' && asString !== true) {
47393             var fd = (new FormData(this.el.dom)).entries();
47394             var ret = {};
47395             var ent = fd.next();
47396             while (!ent.done) {
47397                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47398                 ent = fd.next();
47399             };
47400             return ret;
47401         }
47402         
47403         
47404         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47405         if(asString === true){
47406             return fs;
47407         }
47408         return Roo.urlDecode(fs);
47409     },
47410     
47411     /**
47412      * Returns the fields in this form as an object with key/value pairs. 
47413      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47414      * @return {Object}
47415      */
47416     getFieldValues : function(with_hidden)
47417     {
47418         if (this.childForms) {
47419             // copy values from the child forms
47420             // should this call getFieldValues - probably not as we do not currently copy
47421             // hidden fields when we generate..
47422             Roo.each(this.childForms, function (f) {
47423                 this.setValues(f.getValues());
47424             }, this);
47425         }
47426         
47427         var ret = {};
47428         this.items.each(function(f){
47429             if (!f.getName()) {
47430                 return;
47431             }
47432             var v = f.getValue();
47433             if (f.inputType =='radio') {
47434                 if (typeof(ret[f.getName()]) == 'undefined') {
47435                     ret[f.getName()] = ''; // empty..
47436                 }
47437                 
47438                 if (!f.el.dom.checked) {
47439                     return;
47440                     
47441                 }
47442                 v = f.el.dom.value;
47443                 
47444             }
47445             
47446             // not sure if this supported any more..
47447             if ((typeof(v) == 'object') && f.getRawValue) {
47448                 v = f.getRawValue() ; // dates..
47449             }
47450             // combo boxes where name != hiddenName...
47451             if (f.name != f.getName()) {
47452                 ret[f.name] = f.getRawValue();
47453             }
47454             ret[f.getName()] = v;
47455         });
47456         
47457         return ret;
47458     },
47459
47460     /**
47461      * Clears all invalid messages in this form.
47462      * @return {BasicForm} this
47463      */
47464     clearInvalid : function(){
47465         this.items.each(function(f){
47466            f.clearInvalid();
47467         });
47468         
47469         Roo.each(this.childForms || [], function (f) {
47470             f.clearInvalid();
47471         });
47472         
47473         
47474         return this;
47475     },
47476
47477     /**
47478      * Resets this form.
47479      * @return {BasicForm} this
47480      */
47481     reset : function(){
47482         this.items.each(function(f){
47483             f.reset();
47484         });
47485         
47486         Roo.each(this.childForms || [], function (f) {
47487             f.reset();
47488         });
47489         this.resetHasChanged();
47490         
47491         return this;
47492     },
47493
47494     /**
47495      * Add Roo.form components to this form.
47496      * @param {Field} field1
47497      * @param {Field} field2 (optional)
47498      * @param {Field} etc (optional)
47499      * @return {BasicForm} this
47500      */
47501     add : function(){
47502         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47503         return this;
47504     },
47505
47506
47507     /**
47508      * Removes a field from the items collection (does NOT remove its markup).
47509      * @param {Field} field
47510      * @return {BasicForm} this
47511      */
47512     remove : function(field){
47513         this.items.remove(field);
47514         return this;
47515     },
47516
47517     /**
47518      * Looks at the fields in this form, checks them for an id attribute,
47519      * and calls applyTo on the existing dom element with that id.
47520      * @return {BasicForm} this
47521      */
47522     render : function(){
47523         this.items.each(function(f){
47524             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47525                 f.applyTo(f.id);
47526             }
47527         });
47528         return this;
47529     },
47530
47531     /**
47532      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47533      * @param {Object} values
47534      * @return {BasicForm} this
47535      */
47536     applyToFields : function(o){
47537         this.items.each(function(f){
47538            Roo.apply(f, o);
47539         });
47540         return this;
47541     },
47542
47543     /**
47544      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47545      * @param {Object} values
47546      * @return {BasicForm} this
47547      */
47548     applyIfToFields : function(o){
47549         this.items.each(function(f){
47550            Roo.applyIf(f, o);
47551         });
47552         return this;
47553     }
47554 });
47555
47556 // back compat
47557 Roo.BasicForm = Roo.form.BasicForm;
47558
47559 Roo.apply(Roo.form.BasicForm, {
47560     
47561     popover : {
47562         
47563         padding : 5,
47564         
47565         isApplied : false,
47566         
47567         isMasked : false,
47568         
47569         form : false,
47570         
47571         target : false,
47572         
47573         intervalID : false,
47574         
47575         maskEl : false,
47576         
47577         apply : function()
47578         {
47579             if(this.isApplied){
47580                 return;
47581             }
47582             
47583             this.maskEl = {
47584                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47585                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47586                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47587                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47588             };
47589             
47590             this.maskEl.top.enableDisplayMode("block");
47591             this.maskEl.left.enableDisplayMode("block");
47592             this.maskEl.bottom.enableDisplayMode("block");
47593             this.maskEl.right.enableDisplayMode("block");
47594             
47595             Roo.get(document.body).on('click', function(){
47596                 this.unmask();
47597             }, this);
47598             
47599             Roo.get(document.body).on('touchstart', function(){
47600                 this.unmask();
47601             }, this);
47602             
47603             this.isApplied = true
47604         },
47605         
47606         mask : function(form, target)
47607         {
47608             this.form = form;
47609             
47610             this.target = target;
47611             
47612             if(!this.form.errorMask || !target.el){
47613                 return;
47614             }
47615             
47616             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47617             
47618             var ot = this.target.el.calcOffsetsTo(scrollable);
47619             
47620             var scrollTo = ot[1] - this.form.maskOffset;
47621             
47622             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47623             
47624             scrollable.scrollTo('top', scrollTo);
47625             
47626             var el = this.target.wrap || this.target.el;
47627             
47628             var box = el.getBox();
47629             
47630             this.maskEl.top.setStyle('position', 'absolute');
47631             this.maskEl.top.setStyle('z-index', 10000);
47632             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47633             this.maskEl.top.setLeft(0);
47634             this.maskEl.top.setTop(0);
47635             this.maskEl.top.show();
47636             
47637             this.maskEl.left.setStyle('position', 'absolute');
47638             this.maskEl.left.setStyle('z-index', 10000);
47639             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47640             this.maskEl.left.setLeft(0);
47641             this.maskEl.left.setTop(box.y - this.padding);
47642             this.maskEl.left.show();
47643
47644             this.maskEl.bottom.setStyle('position', 'absolute');
47645             this.maskEl.bottom.setStyle('z-index', 10000);
47646             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47647             this.maskEl.bottom.setLeft(0);
47648             this.maskEl.bottom.setTop(box.bottom + this.padding);
47649             this.maskEl.bottom.show();
47650
47651             this.maskEl.right.setStyle('position', 'absolute');
47652             this.maskEl.right.setStyle('z-index', 10000);
47653             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
47654             this.maskEl.right.setLeft(box.right + this.padding);
47655             this.maskEl.right.setTop(box.y - this.padding);
47656             this.maskEl.right.show();
47657
47658             this.intervalID = window.setInterval(function() {
47659                 Roo.form.BasicForm.popover.unmask();
47660             }, 10000);
47661
47662             window.onwheel = function(){ return false;};
47663             
47664             (function(){ this.isMasked = true; }).defer(500, this);
47665             
47666         },
47667         
47668         unmask : function()
47669         {
47670             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
47671                 return;
47672             }
47673             
47674             this.maskEl.top.setStyle('position', 'absolute');
47675             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
47676             this.maskEl.top.hide();
47677
47678             this.maskEl.left.setStyle('position', 'absolute');
47679             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
47680             this.maskEl.left.hide();
47681
47682             this.maskEl.bottom.setStyle('position', 'absolute');
47683             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
47684             this.maskEl.bottom.hide();
47685
47686             this.maskEl.right.setStyle('position', 'absolute');
47687             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
47688             this.maskEl.right.hide();
47689             
47690             window.onwheel = function(){ return true;};
47691             
47692             if(this.intervalID){
47693                 window.clearInterval(this.intervalID);
47694                 this.intervalID = false;
47695             }
47696             
47697             this.isMasked = false;
47698             
47699         }
47700         
47701     }
47702     
47703 });/*
47704  * Based on:
47705  * Ext JS Library 1.1.1
47706  * Copyright(c) 2006-2007, Ext JS, LLC.
47707  *
47708  * Originally Released Under LGPL - original licence link has changed is not relivant.
47709  *
47710  * Fork - LGPL
47711  * <script type="text/javascript">
47712  */
47713
47714 /**
47715  * @class Roo.form.Form
47716  * @extends Roo.form.BasicForm
47717  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47718  * @constructor
47719  * @param {Object} config Configuration options
47720  */
47721 Roo.form.Form = function(config){
47722     var xitems =  [];
47723     if (config.items) {
47724         xitems = config.items;
47725         delete config.items;
47726     }
47727    
47728     
47729     Roo.form.Form.superclass.constructor.call(this, null, config);
47730     this.url = this.url || this.action;
47731     if(!this.root){
47732         this.root = new Roo.form.Layout(Roo.applyIf({
47733             id: Roo.id()
47734         }, config));
47735     }
47736     this.active = this.root;
47737     /**
47738      * Array of all the buttons that have been added to this form via {@link addButton}
47739      * @type Array
47740      */
47741     this.buttons = [];
47742     this.allItems = [];
47743     this.addEvents({
47744         /**
47745          * @event clientvalidation
47746          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47747          * @param {Form} this
47748          * @param {Boolean} valid true if the form has passed client-side validation
47749          */
47750         clientvalidation: true,
47751         /**
47752          * @event rendered
47753          * Fires when the form is rendered
47754          * @param {Roo.form.Form} form
47755          */
47756         rendered : true
47757     });
47758     
47759     if (this.progressUrl) {
47760             // push a hidden field onto the list of fields..
47761             this.addxtype( {
47762                     xns: Roo.form, 
47763                     xtype : 'Hidden', 
47764                     name : 'UPLOAD_IDENTIFIER' 
47765             });
47766         }
47767         
47768     
47769     Roo.each(xitems, this.addxtype, this);
47770     
47771 };
47772
47773 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47774     /**
47775      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47776      */
47777     /**
47778      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47779      */
47780     /**
47781      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47782      */
47783     buttonAlign:'center',
47784
47785     /**
47786      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47787      */
47788     minButtonWidth:75,
47789
47790     /**
47791      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47792      * This property cascades to child containers if not set.
47793      */
47794     labelAlign:'left',
47795
47796     /**
47797      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47798      * fires a looping event with that state. This is required to bind buttons to the valid
47799      * state using the config value formBind:true on the button.
47800      */
47801     monitorValid : false,
47802
47803     /**
47804      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47805      */
47806     monitorPoll : 200,
47807     
47808     /**
47809      * @cfg {String} progressUrl - Url to return progress data 
47810      */
47811     
47812     progressUrl : false,
47813     /**
47814      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47815      * sending a formdata with extra parameters - eg uploaded elements.
47816      */
47817     
47818     formData : false,
47819     
47820     /**
47821      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47822      * fields are added and the column is closed. If no fields are passed the column remains open
47823      * until end() is called.
47824      * @param {Object} config The config to pass to the column
47825      * @param {Field} field1 (optional)
47826      * @param {Field} field2 (optional)
47827      * @param {Field} etc (optional)
47828      * @return Column The column container object
47829      */
47830     column : function(c){
47831         var col = new Roo.form.Column(c);
47832         this.start(col);
47833         if(arguments.length > 1){ // duplicate code required because of Opera
47834             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47835             this.end();
47836         }
47837         return col;
47838     },
47839
47840     /**
47841      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47842      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47843      * until end() is called.
47844      * @param {Object} config The config to pass to the fieldset
47845      * @param {Field} field1 (optional)
47846      * @param {Field} field2 (optional)
47847      * @param {Field} etc (optional)
47848      * @return FieldSet The fieldset container object
47849      */
47850     fieldset : function(c){
47851         var fs = new Roo.form.FieldSet(c);
47852         this.start(fs);
47853         if(arguments.length > 1){ // duplicate code required because of Opera
47854             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47855             this.end();
47856         }
47857         return fs;
47858     },
47859
47860     /**
47861      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47862      * fields are added and the container is closed. If no fields are passed the container remains open
47863      * until end() is called.
47864      * @param {Object} config The config to pass to the Layout
47865      * @param {Field} field1 (optional)
47866      * @param {Field} field2 (optional)
47867      * @param {Field} etc (optional)
47868      * @return Layout The container object
47869      */
47870     container : function(c){
47871         var l = new Roo.form.Layout(c);
47872         this.start(l);
47873         if(arguments.length > 1){ // duplicate code required because of Opera
47874             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47875             this.end();
47876         }
47877         return l;
47878     },
47879
47880     /**
47881      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47882      * @param {Object} container A Roo.form.Layout or subclass of Layout
47883      * @return {Form} this
47884      */
47885     start : function(c){
47886         // cascade label info
47887         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47888         this.active.stack.push(c);
47889         c.ownerCt = this.active;
47890         this.active = c;
47891         return this;
47892     },
47893
47894     /**
47895      * Closes the current open container
47896      * @return {Form} this
47897      */
47898     end : function(){
47899         if(this.active == this.root){
47900             return this;
47901         }
47902         this.active = this.active.ownerCt;
47903         return this;
47904     },
47905
47906     /**
47907      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47908      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47909      * as the label of the field.
47910      * @param {Field} field1
47911      * @param {Field} field2 (optional)
47912      * @param {Field} etc. (optional)
47913      * @return {Form} this
47914      */
47915     add : function(){
47916         this.active.stack.push.apply(this.active.stack, arguments);
47917         this.allItems.push.apply(this.allItems,arguments);
47918         var r = [];
47919         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47920             if(a[i].isFormField){
47921                 r.push(a[i]);
47922             }
47923         }
47924         if(r.length > 0){
47925             Roo.form.Form.superclass.add.apply(this, r);
47926         }
47927         return this;
47928     },
47929     
47930
47931     
47932     
47933     
47934      /**
47935      * Find any element that has been added to a form, using it's ID or name
47936      * This can include framesets, columns etc. along with regular fields..
47937      * @param {String} id - id or name to find.
47938      
47939      * @return {Element} e - or false if nothing found.
47940      */
47941     findbyId : function(id)
47942     {
47943         var ret = false;
47944         if (!id) {
47945             return ret;
47946         }
47947         Roo.each(this.allItems, function(f){
47948             if (f.id == id || f.name == id ){
47949                 ret = f;
47950                 return false;
47951             }
47952         });
47953         return ret;
47954     },
47955
47956     
47957     
47958     /**
47959      * Render this form into the passed container. This should only be called once!
47960      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47961      * @return {Form} this
47962      */
47963     render : function(ct)
47964     {
47965         
47966         
47967         
47968         ct = Roo.get(ct);
47969         var o = this.autoCreate || {
47970             tag: 'form',
47971             method : this.method || 'POST',
47972             id : this.id || Roo.id()
47973         };
47974         this.initEl(ct.createChild(o));
47975
47976         this.root.render(this.el);
47977         
47978        
47979              
47980         this.items.each(function(f){
47981             f.render('x-form-el-'+f.id);
47982         });
47983
47984         if(this.buttons.length > 0){
47985             // tables are required to maintain order and for correct IE layout
47986             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47987                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47988                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47989             }}, null, true);
47990             var tr = tb.getElementsByTagName('tr')[0];
47991             for(var i = 0, len = this.buttons.length; i < len; i++) {
47992                 var b = this.buttons[i];
47993                 var td = document.createElement('td');
47994                 td.className = 'x-form-btn-td';
47995                 b.render(tr.appendChild(td));
47996             }
47997         }
47998         if(this.monitorValid){ // initialize after render
47999             this.startMonitoring();
48000         }
48001         this.fireEvent('rendered', this);
48002         return this;
48003     },
48004
48005     /**
48006      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48007      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48008      * object or a valid Roo.DomHelper element config
48009      * @param {Function} handler The function called when the button is clicked
48010      * @param {Object} scope (optional) The scope of the handler function
48011      * @return {Roo.Button}
48012      */
48013     addButton : function(config, handler, scope){
48014         var bc = {
48015             handler: handler,
48016             scope: scope,
48017             minWidth: this.minButtonWidth,
48018             hideParent:true
48019         };
48020         if(typeof config == "string"){
48021             bc.text = config;
48022         }else{
48023             Roo.apply(bc, config);
48024         }
48025         var btn = new Roo.Button(null, bc);
48026         this.buttons.push(btn);
48027         return btn;
48028     },
48029
48030      /**
48031      * Adds a series of form elements (using the xtype property as the factory method.
48032      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48033      * @param {Object} config 
48034      */
48035     
48036     addxtype : function()
48037     {
48038         var ar = Array.prototype.slice.call(arguments, 0);
48039         var ret = false;
48040         for(var i = 0; i < ar.length; i++) {
48041             if (!ar[i]) {
48042                 continue; // skip -- if this happends something invalid got sent, we 
48043                 // should ignore it, as basically that interface element will not show up
48044                 // and that should be pretty obvious!!
48045             }
48046             
48047             if (Roo.form[ar[i].xtype]) {
48048                 ar[i].form = this;
48049                 var fe = Roo.factory(ar[i], Roo.form);
48050                 if (!ret) {
48051                     ret = fe;
48052                 }
48053                 fe.form = this;
48054                 if (fe.store) {
48055                     fe.store.form = this;
48056                 }
48057                 if (fe.isLayout) {  
48058                          
48059                     this.start(fe);
48060                     this.allItems.push(fe);
48061                     if (fe.items && fe.addxtype) {
48062                         fe.addxtype.apply(fe, fe.items);
48063                         delete fe.items;
48064                     }
48065                      this.end();
48066                     continue;
48067                 }
48068                 
48069                 
48070                  
48071                 this.add(fe);
48072               //  console.log('adding ' + ar[i].xtype);
48073             }
48074             if (ar[i].xtype == 'Button') {  
48075                 //console.log('adding button');
48076                 //console.log(ar[i]);
48077                 this.addButton(ar[i]);
48078                 this.allItems.push(fe);
48079                 continue;
48080             }
48081             
48082             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48083                 alert('end is not supported on xtype any more, use items');
48084             //    this.end();
48085             //    //console.log('adding end');
48086             }
48087             
48088         }
48089         return ret;
48090     },
48091     
48092     /**
48093      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48094      * option "monitorValid"
48095      */
48096     startMonitoring : function(){
48097         if(!this.bound){
48098             this.bound = true;
48099             Roo.TaskMgr.start({
48100                 run : this.bindHandler,
48101                 interval : this.monitorPoll || 200,
48102                 scope: this
48103             });
48104         }
48105     },
48106
48107     /**
48108      * Stops monitoring of the valid state of this form
48109      */
48110     stopMonitoring : function(){
48111         this.bound = false;
48112     },
48113
48114     // private
48115     bindHandler : function(){
48116         if(!this.bound){
48117             return false; // stops binding
48118         }
48119         var valid = true;
48120         this.items.each(function(f){
48121             if(!f.isValid(true)){
48122                 valid = false;
48123                 return false;
48124             }
48125         });
48126         for(var i = 0, len = this.buttons.length; i < len; i++){
48127             var btn = this.buttons[i];
48128             if(btn.formBind === true && btn.disabled === valid){
48129                 btn.setDisabled(!valid);
48130             }
48131         }
48132         this.fireEvent('clientvalidation', this, valid);
48133     }
48134     
48135     
48136     
48137     
48138     
48139     
48140     
48141     
48142 });
48143
48144
48145 // back compat
48146 Roo.Form = Roo.form.Form;
48147 /*
48148  * Based on:
48149  * Ext JS Library 1.1.1
48150  * Copyright(c) 2006-2007, Ext JS, LLC.
48151  *
48152  * Originally Released Under LGPL - original licence link has changed is not relivant.
48153  *
48154  * Fork - LGPL
48155  * <script type="text/javascript">
48156  */
48157
48158 // as we use this in bootstrap.
48159 Roo.namespace('Roo.form');
48160  /**
48161  * @class Roo.form.Action
48162  * Internal Class used to handle form actions
48163  * @constructor
48164  * @param {Roo.form.BasicForm} el The form element or its id
48165  * @param {Object} config Configuration options
48166  */
48167
48168  
48169  
48170 // define the action interface
48171 Roo.form.Action = function(form, options){
48172     this.form = form;
48173     this.options = options || {};
48174 };
48175 /**
48176  * Client Validation Failed
48177  * @const 
48178  */
48179 Roo.form.Action.CLIENT_INVALID = 'client';
48180 /**
48181  * Server Validation Failed
48182  * @const 
48183  */
48184 Roo.form.Action.SERVER_INVALID = 'server';
48185  /**
48186  * Connect to Server Failed
48187  * @const 
48188  */
48189 Roo.form.Action.CONNECT_FAILURE = 'connect';
48190 /**
48191  * Reading Data from Server Failed
48192  * @const 
48193  */
48194 Roo.form.Action.LOAD_FAILURE = 'load';
48195
48196 Roo.form.Action.prototype = {
48197     type : 'default',
48198     failureType : undefined,
48199     response : undefined,
48200     result : undefined,
48201
48202     // interface method
48203     run : function(options){
48204
48205     },
48206
48207     // interface method
48208     success : function(response){
48209
48210     },
48211
48212     // interface method
48213     handleResponse : function(response){
48214
48215     },
48216
48217     // default connection failure
48218     failure : function(response){
48219         
48220         this.response = response;
48221         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48222         this.form.afterAction(this, false);
48223     },
48224
48225     processResponse : function(response){
48226         this.response = response;
48227         if(!response.responseText){
48228             return true;
48229         }
48230         this.result = this.handleResponse(response);
48231         return this.result;
48232     },
48233
48234     // utility functions used internally
48235     getUrl : function(appendParams){
48236         var url = this.options.url || this.form.url || this.form.el.dom.action;
48237         if(appendParams){
48238             var p = this.getParams();
48239             if(p){
48240                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48241             }
48242         }
48243         return url;
48244     },
48245
48246     getMethod : function(){
48247         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48248     },
48249
48250     getParams : function(){
48251         var bp = this.form.baseParams;
48252         var p = this.options.params;
48253         if(p){
48254             if(typeof p == "object"){
48255                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48256             }else if(typeof p == 'string' && bp){
48257                 p += '&' + Roo.urlEncode(bp);
48258             }
48259         }else if(bp){
48260             p = Roo.urlEncode(bp);
48261         }
48262         return p;
48263     },
48264
48265     createCallback : function(){
48266         return {
48267             success: this.success,
48268             failure: this.failure,
48269             scope: this,
48270             timeout: (this.form.timeout*1000),
48271             upload: this.form.fileUpload ? this.success : undefined
48272         };
48273     }
48274 };
48275
48276 Roo.form.Action.Submit = function(form, options){
48277     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48278 };
48279
48280 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48281     type : 'submit',
48282
48283     haveProgress : false,
48284     uploadComplete : false,
48285     
48286     // uploadProgress indicator.
48287     uploadProgress : function()
48288     {
48289         if (!this.form.progressUrl) {
48290             return;
48291         }
48292         
48293         if (!this.haveProgress) {
48294             Roo.MessageBox.progress("Uploading", "Uploading");
48295         }
48296         if (this.uploadComplete) {
48297            Roo.MessageBox.hide();
48298            return;
48299         }
48300         
48301         this.haveProgress = true;
48302    
48303         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48304         
48305         var c = new Roo.data.Connection();
48306         c.request({
48307             url : this.form.progressUrl,
48308             params: {
48309                 id : uid
48310             },
48311             method: 'GET',
48312             success : function(req){
48313                //console.log(data);
48314                 var rdata = false;
48315                 var edata;
48316                 try  {
48317                    rdata = Roo.decode(req.responseText)
48318                 } catch (e) {
48319                     Roo.log("Invalid data from server..");
48320                     Roo.log(edata);
48321                     return;
48322                 }
48323                 if (!rdata || !rdata.success) {
48324                     Roo.log(rdata);
48325                     Roo.MessageBox.alert(Roo.encode(rdata));
48326                     return;
48327                 }
48328                 var data = rdata.data;
48329                 
48330                 if (this.uploadComplete) {
48331                    Roo.MessageBox.hide();
48332                    return;
48333                 }
48334                    
48335                 if (data){
48336                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48337                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48338                     );
48339                 }
48340                 this.uploadProgress.defer(2000,this);
48341             },
48342        
48343             failure: function(data) {
48344                 Roo.log('progress url failed ');
48345                 Roo.log(data);
48346             },
48347             scope : this
48348         });
48349            
48350     },
48351     
48352     
48353     run : function()
48354     {
48355         // run get Values on the form, so it syncs any secondary forms.
48356         this.form.getValues();
48357         
48358         var o = this.options;
48359         var method = this.getMethod();
48360         var isPost = method == 'POST';
48361         if(o.clientValidation === false || this.form.isValid()){
48362             
48363             if (this.form.progressUrl) {
48364                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48365                     (new Date() * 1) + '' + Math.random());
48366                     
48367             } 
48368             
48369             
48370             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48371                 form:this.form.el.dom,
48372                 url:this.getUrl(!isPost),
48373                 method: method,
48374                 params:isPost ? this.getParams() : null,
48375                 isUpload: this.form.fileUpload,
48376                 formData : this.form.formData
48377             }));
48378             
48379             this.uploadProgress();
48380
48381         }else if (o.clientValidation !== false){ // client validation failed
48382             this.failureType = Roo.form.Action.CLIENT_INVALID;
48383             this.form.afterAction(this, false);
48384         }
48385     },
48386
48387     success : function(response)
48388     {
48389         this.uploadComplete= true;
48390         if (this.haveProgress) {
48391             Roo.MessageBox.hide();
48392         }
48393         
48394         
48395         var result = this.processResponse(response);
48396         if(result === true || result.success){
48397             this.form.afterAction(this, true);
48398             return;
48399         }
48400         if(result.errors){
48401             this.form.markInvalid(result.errors);
48402             this.failureType = Roo.form.Action.SERVER_INVALID;
48403         }
48404         this.form.afterAction(this, false);
48405     },
48406     failure : function(response)
48407     {
48408         this.uploadComplete= true;
48409         if (this.haveProgress) {
48410             Roo.MessageBox.hide();
48411         }
48412         
48413         this.response = response;
48414         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48415         this.form.afterAction(this, false);
48416     },
48417     
48418     handleResponse : function(response){
48419         if(this.form.errorReader){
48420             var rs = this.form.errorReader.read(response);
48421             var errors = [];
48422             if(rs.records){
48423                 for(var i = 0, len = rs.records.length; i < len; i++) {
48424                     var r = rs.records[i];
48425                     errors[i] = r.data;
48426                 }
48427             }
48428             if(errors.length < 1){
48429                 errors = null;
48430             }
48431             return {
48432                 success : rs.success,
48433                 errors : errors
48434             };
48435         }
48436         var ret = false;
48437         try {
48438             ret = Roo.decode(response.responseText);
48439         } catch (e) {
48440             ret = {
48441                 success: false,
48442                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48443                 errors : []
48444             };
48445         }
48446         return ret;
48447         
48448     }
48449 });
48450
48451
48452 Roo.form.Action.Load = function(form, options){
48453     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48454     this.reader = this.form.reader;
48455 };
48456
48457 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48458     type : 'load',
48459
48460     run : function(){
48461         
48462         Roo.Ajax.request(Roo.apply(
48463                 this.createCallback(), {
48464                     method:this.getMethod(),
48465                     url:this.getUrl(false),
48466                     params:this.getParams()
48467         }));
48468     },
48469
48470     success : function(response){
48471         
48472         var result = this.processResponse(response);
48473         if(result === true || !result.success || !result.data){
48474             this.failureType = Roo.form.Action.LOAD_FAILURE;
48475             this.form.afterAction(this, false);
48476             return;
48477         }
48478         this.form.clearInvalid();
48479         this.form.setValues(result.data);
48480         this.form.afterAction(this, true);
48481     },
48482
48483     handleResponse : function(response){
48484         if(this.form.reader){
48485             var rs = this.form.reader.read(response);
48486             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48487             return {
48488                 success : rs.success,
48489                 data : data
48490             };
48491         }
48492         return Roo.decode(response.responseText);
48493     }
48494 });
48495
48496 Roo.form.Action.ACTION_TYPES = {
48497     'load' : Roo.form.Action.Load,
48498     'submit' : Roo.form.Action.Submit
48499 };/*
48500  * Based on:
48501  * Ext JS Library 1.1.1
48502  * Copyright(c) 2006-2007, Ext JS, LLC.
48503  *
48504  * Originally Released Under LGPL - original licence link has changed is not relivant.
48505  *
48506  * Fork - LGPL
48507  * <script type="text/javascript">
48508  */
48509  
48510 /**
48511  * @class Roo.form.Layout
48512  * @extends Roo.Component
48513  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48514  * @constructor
48515  * @param {Object} config Configuration options
48516  */
48517 Roo.form.Layout = function(config){
48518     var xitems = [];
48519     if (config.items) {
48520         xitems = config.items;
48521         delete config.items;
48522     }
48523     Roo.form.Layout.superclass.constructor.call(this, config);
48524     this.stack = [];
48525     Roo.each(xitems, this.addxtype, this);
48526      
48527 };
48528
48529 Roo.extend(Roo.form.Layout, Roo.Component, {
48530     /**
48531      * @cfg {String/Object} autoCreate
48532      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48533      */
48534     /**
48535      * @cfg {String/Object/Function} style
48536      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48537      * a function which returns such a specification.
48538      */
48539     /**
48540      * @cfg {String} labelAlign
48541      * Valid values are "left," "top" and "right" (defaults to "left")
48542      */
48543     /**
48544      * @cfg {Number} labelWidth
48545      * Fixed width in pixels of all field labels (defaults to undefined)
48546      */
48547     /**
48548      * @cfg {Boolean} clear
48549      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48550      */
48551     clear : true,
48552     /**
48553      * @cfg {String} labelSeparator
48554      * The separator to use after field labels (defaults to ':')
48555      */
48556     labelSeparator : ':',
48557     /**
48558      * @cfg {Boolean} hideLabels
48559      * True to suppress the display of field labels in this layout (defaults to false)
48560      */
48561     hideLabels : false,
48562
48563     // private
48564     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48565     
48566     isLayout : true,
48567     
48568     // private
48569     onRender : function(ct, position){
48570         if(this.el){ // from markup
48571             this.el = Roo.get(this.el);
48572         }else {  // generate
48573             var cfg = this.getAutoCreate();
48574             this.el = ct.createChild(cfg, position);
48575         }
48576         if(this.style){
48577             this.el.applyStyles(this.style);
48578         }
48579         if(this.labelAlign){
48580             this.el.addClass('x-form-label-'+this.labelAlign);
48581         }
48582         if(this.hideLabels){
48583             this.labelStyle = "display:none";
48584             this.elementStyle = "padding-left:0;";
48585         }else{
48586             if(typeof this.labelWidth == 'number'){
48587                 this.labelStyle = "width:"+this.labelWidth+"px;";
48588                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48589             }
48590             if(this.labelAlign == 'top'){
48591                 this.labelStyle = "width:auto;";
48592                 this.elementStyle = "padding-left:0;";
48593             }
48594         }
48595         var stack = this.stack;
48596         var slen = stack.length;
48597         if(slen > 0){
48598             if(!this.fieldTpl){
48599                 var t = new Roo.Template(
48600                     '<div class="x-form-item {5}">',
48601                         '<label for="{0}" style="{2}">{1}{4}</label>',
48602                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48603                         '</div>',
48604                     '</div><div class="x-form-clear-left"></div>'
48605                 );
48606                 t.disableFormats = true;
48607                 t.compile();
48608                 Roo.form.Layout.prototype.fieldTpl = t;
48609             }
48610             for(var i = 0; i < slen; i++) {
48611                 if(stack[i].isFormField){
48612                     this.renderField(stack[i]);
48613                 }else{
48614                     this.renderComponent(stack[i]);
48615                 }
48616             }
48617         }
48618         if(this.clear){
48619             this.el.createChild({cls:'x-form-clear'});
48620         }
48621     },
48622
48623     // private
48624     renderField : function(f){
48625         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48626                f.id, //0
48627                f.fieldLabel, //1
48628                f.labelStyle||this.labelStyle||'', //2
48629                this.elementStyle||'', //3
48630                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48631                f.itemCls||this.itemCls||''  //5
48632        ], true).getPrevSibling());
48633     },
48634
48635     // private
48636     renderComponent : function(c){
48637         c.render(c.isLayout ? this.el : this.el.createChild());    
48638     },
48639     /**
48640      * Adds a object form elements (using the xtype property as the factory method.)
48641      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48642      * @param {Object} config 
48643      */
48644     addxtype : function(o)
48645     {
48646         // create the lement.
48647         o.form = this.form;
48648         var fe = Roo.factory(o, Roo.form);
48649         this.form.allItems.push(fe);
48650         this.stack.push(fe);
48651         
48652         if (fe.isFormField) {
48653             this.form.items.add(fe);
48654         }
48655          
48656         return fe;
48657     }
48658 });
48659
48660 /**
48661  * @class Roo.form.Column
48662  * @extends Roo.form.Layout
48663  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48664  * @constructor
48665  * @param {Object} config Configuration options
48666  */
48667 Roo.form.Column = function(config){
48668     Roo.form.Column.superclass.constructor.call(this, config);
48669 };
48670
48671 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48672     /**
48673      * @cfg {Number/String} width
48674      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48675      */
48676     /**
48677      * @cfg {String/Object} autoCreate
48678      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48679      */
48680
48681     // private
48682     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48683
48684     // private
48685     onRender : function(ct, position){
48686         Roo.form.Column.superclass.onRender.call(this, ct, position);
48687         if(this.width){
48688             this.el.setWidth(this.width);
48689         }
48690     }
48691 });
48692
48693
48694 /**
48695  * @class Roo.form.Row
48696  * @extends Roo.form.Layout
48697  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48698  * @constructor
48699  * @param {Object} config Configuration options
48700  */
48701
48702  
48703 Roo.form.Row = function(config){
48704     Roo.form.Row.superclass.constructor.call(this, config);
48705 };
48706  
48707 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48708       /**
48709      * @cfg {Number/String} width
48710      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48711      */
48712     /**
48713      * @cfg {Number/String} height
48714      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48715      */
48716     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48717     
48718     padWidth : 20,
48719     // private
48720     onRender : function(ct, position){
48721         //console.log('row render');
48722         if(!this.rowTpl){
48723             var t = new Roo.Template(
48724                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48725                     '<label for="{0}" style="{2}">{1}{4}</label>',
48726                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48727                     '</div>',
48728                 '</div>'
48729             );
48730             t.disableFormats = true;
48731             t.compile();
48732             Roo.form.Layout.prototype.rowTpl = t;
48733         }
48734         this.fieldTpl = this.rowTpl;
48735         
48736         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48737         var labelWidth = 100;
48738         
48739         if ((this.labelAlign != 'top')) {
48740             if (typeof this.labelWidth == 'number') {
48741                 labelWidth = this.labelWidth
48742             }
48743             this.padWidth =  20 + labelWidth;
48744             
48745         }
48746         
48747         Roo.form.Column.superclass.onRender.call(this, ct, position);
48748         if(this.width){
48749             this.el.setWidth(this.width);
48750         }
48751         if(this.height){
48752             this.el.setHeight(this.height);
48753         }
48754     },
48755     
48756     // private
48757     renderField : function(f){
48758         f.fieldEl = this.fieldTpl.append(this.el, [
48759                f.id, f.fieldLabel,
48760                f.labelStyle||this.labelStyle||'',
48761                this.elementStyle||'',
48762                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48763                f.itemCls||this.itemCls||'',
48764                f.width ? f.width + this.padWidth : 160 + this.padWidth
48765        ],true);
48766     }
48767 });
48768  
48769
48770 /**
48771  * @class Roo.form.FieldSet
48772  * @extends Roo.form.Layout
48773  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48774  * @constructor
48775  * @param {Object} config Configuration options
48776  */
48777 Roo.form.FieldSet = function(config){
48778     Roo.form.FieldSet.superclass.constructor.call(this, config);
48779 };
48780
48781 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48782     /**
48783      * @cfg {String} legend
48784      * The text to display as the legend for the FieldSet (defaults to '')
48785      */
48786     /**
48787      * @cfg {String/Object} autoCreate
48788      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48789      */
48790
48791     // private
48792     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48793
48794     // private
48795     onRender : function(ct, position){
48796         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48797         if(this.legend){
48798             this.setLegend(this.legend);
48799         }
48800     },
48801
48802     // private
48803     setLegend : function(text){
48804         if(this.rendered){
48805             this.el.child('legend').update(text);
48806         }
48807     }
48808 });/*
48809  * Based on:
48810  * Ext JS Library 1.1.1
48811  * Copyright(c) 2006-2007, Ext JS, LLC.
48812  *
48813  * Originally Released Under LGPL - original licence link has changed is not relivant.
48814  *
48815  * Fork - LGPL
48816  * <script type="text/javascript">
48817  */
48818 /**
48819  * @class Roo.form.VTypes
48820  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48821  * @singleton
48822  */
48823 Roo.form.VTypes = function(){
48824     // closure these in so they are only created once.
48825     var alpha = /^[a-zA-Z_]+$/;
48826     var alphanum = /^[a-zA-Z0-9_]+$/;
48827     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48828     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48829
48830     // All these messages and functions are configurable
48831     return {
48832         /**
48833          * The function used to validate email addresses
48834          * @param {String} value The email address
48835          */
48836         'email' : function(v){
48837             return email.test(v);
48838         },
48839         /**
48840          * The error text to display when the email validation function returns false
48841          * @type String
48842          */
48843         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48844         /**
48845          * The keystroke filter mask to be applied on email input
48846          * @type RegExp
48847          */
48848         'emailMask' : /[a-z0-9_\.\-@]/i,
48849
48850         /**
48851          * The function used to validate URLs
48852          * @param {String} value The URL
48853          */
48854         'url' : function(v){
48855             return url.test(v);
48856         },
48857         /**
48858          * The error text to display when the url validation function returns false
48859          * @type String
48860          */
48861         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48862         
48863         /**
48864          * The function used to validate alpha values
48865          * @param {String} value The value
48866          */
48867         'alpha' : function(v){
48868             return alpha.test(v);
48869         },
48870         /**
48871          * The error text to display when the alpha validation function returns false
48872          * @type String
48873          */
48874         'alphaText' : 'This field should only contain letters and _',
48875         /**
48876          * The keystroke filter mask to be applied on alpha input
48877          * @type RegExp
48878          */
48879         'alphaMask' : /[a-z_]/i,
48880
48881         /**
48882          * The function used to validate alphanumeric values
48883          * @param {String} value The value
48884          */
48885         'alphanum' : function(v){
48886             return alphanum.test(v);
48887         },
48888         /**
48889          * The error text to display when the alphanumeric validation function returns false
48890          * @type String
48891          */
48892         'alphanumText' : 'This field should only contain letters, numbers and _',
48893         /**
48894          * The keystroke filter mask to be applied on alphanumeric input
48895          * @type RegExp
48896          */
48897         'alphanumMask' : /[a-z0-9_]/i
48898     };
48899 }();//<script type="text/javascript">
48900
48901 /**
48902  * @class Roo.form.FCKeditor
48903  * @extends Roo.form.TextArea
48904  * Wrapper around the FCKEditor http://www.fckeditor.net
48905  * @constructor
48906  * Creates a new FCKeditor
48907  * @param {Object} config Configuration options
48908  */
48909 Roo.form.FCKeditor = function(config){
48910     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48911     this.addEvents({
48912          /**
48913          * @event editorinit
48914          * Fired when the editor is initialized - you can add extra handlers here..
48915          * @param {FCKeditor} this
48916          * @param {Object} the FCK object.
48917          */
48918         editorinit : true
48919     });
48920     
48921     
48922 };
48923 Roo.form.FCKeditor.editors = { };
48924 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48925 {
48926     //defaultAutoCreate : {
48927     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48928     //},
48929     // private
48930     /**
48931      * @cfg {Object} fck options - see fck manual for details.
48932      */
48933     fckconfig : false,
48934     
48935     /**
48936      * @cfg {Object} fck toolbar set (Basic or Default)
48937      */
48938     toolbarSet : 'Basic',
48939     /**
48940      * @cfg {Object} fck BasePath
48941      */ 
48942     basePath : '/fckeditor/',
48943     
48944     
48945     frame : false,
48946     
48947     value : '',
48948     
48949    
48950     onRender : function(ct, position)
48951     {
48952         if(!this.el){
48953             this.defaultAutoCreate = {
48954                 tag: "textarea",
48955                 style:"width:300px;height:60px;",
48956                 autocomplete: "new-password"
48957             };
48958         }
48959         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48960         /*
48961         if(this.grow){
48962             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48963             if(this.preventScrollbars){
48964                 this.el.setStyle("overflow", "hidden");
48965             }
48966             this.el.setHeight(this.growMin);
48967         }
48968         */
48969         //console.log('onrender' + this.getId() );
48970         Roo.form.FCKeditor.editors[this.getId()] = this;
48971          
48972
48973         this.replaceTextarea() ;
48974         
48975     },
48976     
48977     getEditor : function() {
48978         return this.fckEditor;
48979     },
48980     /**
48981      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48982      * @param {Mixed} value The value to set
48983      */
48984     
48985     
48986     setValue : function(value)
48987     {
48988         //console.log('setValue: ' + value);
48989         
48990         if(typeof(value) == 'undefined') { // not sure why this is happending...
48991             return;
48992         }
48993         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48994         
48995         //if(!this.el || !this.getEditor()) {
48996         //    this.value = value;
48997             //this.setValue.defer(100,this,[value]);    
48998         //    return;
48999         //} 
49000         
49001         if(!this.getEditor()) {
49002             return;
49003         }
49004         
49005         this.getEditor().SetData(value);
49006         
49007         //
49008
49009     },
49010
49011     /**
49012      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49013      * @return {Mixed} value The field value
49014      */
49015     getValue : function()
49016     {
49017         
49018         if (this.frame && this.frame.dom.style.display == 'none') {
49019             return Roo.form.FCKeditor.superclass.getValue.call(this);
49020         }
49021         
49022         if(!this.el || !this.getEditor()) {
49023            
49024            // this.getValue.defer(100,this); 
49025             return this.value;
49026         }
49027        
49028         
49029         var value=this.getEditor().GetData();
49030         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49031         return Roo.form.FCKeditor.superclass.getValue.call(this);
49032         
49033
49034     },
49035
49036     /**
49037      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49038      * @return {Mixed} value The field value
49039      */
49040     getRawValue : function()
49041     {
49042         if (this.frame && this.frame.dom.style.display == 'none') {
49043             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49044         }
49045         
49046         if(!this.el || !this.getEditor()) {
49047             //this.getRawValue.defer(100,this); 
49048             return this.value;
49049             return;
49050         }
49051         
49052         
49053         
49054         var value=this.getEditor().GetData();
49055         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49056         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49057          
49058     },
49059     
49060     setSize : function(w,h) {
49061         
49062         
49063         
49064         //if (this.frame && this.frame.dom.style.display == 'none') {
49065         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49066         //    return;
49067         //}
49068         //if(!this.el || !this.getEditor()) {
49069         //    this.setSize.defer(100,this, [w,h]); 
49070         //    return;
49071         //}
49072         
49073         
49074         
49075         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49076         
49077         this.frame.dom.setAttribute('width', w);
49078         this.frame.dom.setAttribute('height', h);
49079         this.frame.setSize(w,h);
49080         
49081     },
49082     
49083     toggleSourceEdit : function(value) {
49084         
49085       
49086          
49087         this.el.dom.style.display = value ? '' : 'none';
49088         this.frame.dom.style.display = value ?  'none' : '';
49089         
49090     },
49091     
49092     
49093     focus: function(tag)
49094     {
49095         if (this.frame.dom.style.display == 'none') {
49096             return Roo.form.FCKeditor.superclass.focus.call(this);
49097         }
49098         if(!this.el || !this.getEditor()) {
49099             this.focus.defer(100,this, [tag]); 
49100             return;
49101         }
49102         
49103         
49104         
49105         
49106         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49107         this.getEditor().Focus();
49108         if (tgs.length) {
49109             if (!this.getEditor().Selection.GetSelection()) {
49110                 this.focus.defer(100,this, [tag]); 
49111                 return;
49112             }
49113             
49114             
49115             var r = this.getEditor().EditorDocument.createRange();
49116             r.setStart(tgs[0],0);
49117             r.setEnd(tgs[0],0);
49118             this.getEditor().Selection.GetSelection().removeAllRanges();
49119             this.getEditor().Selection.GetSelection().addRange(r);
49120             this.getEditor().Focus();
49121         }
49122         
49123     },
49124     
49125     
49126     
49127     replaceTextarea : function()
49128     {
49129         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49130             return ;
49131         }
49132         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49133         //{
49134             // We must check the elements firstly using the Id and then the name.
49135         var oTextarea = document.getElementById( this.getId() );
49136         
49137         var colElementsByName = document.getElementsByName( this.getId() ) ;
49138          
49139         oTextarea.style.display = 'none' ;
49140
49141         if ( oTextarea.tabIndex ) {            
49142             this.TabIndex = oTextarea.tabIndex ;
49143         }
49144         
49145         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49146         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49147         this.frame = Roo.get(this.getId() + '___Frame')
49148     },
49149     
49150     _getConfigHtml : function()
49151     {
49152         var sConfig = '' ;
49153
49154         for ( var o in this.fckconfig ) {
49155             sConfig += sConfig.length > 0  ? '&amp;' : '';
49156             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49157         }
49158
49159         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49160     },
49161     
49162     
49163     _getIFrameHtml : function()
49164     {
49165         var sFile = 'fckeditor.html' ;
49166         /* no idea what this is about..
49167         try
49168         {
49169             if ( (/fcksource=true/i).test( window.top.location.search ) )
49170                 sFile = 'fckeditor.original.html' ;
49171         }
49172         catch (e) { 
49173         */
49174
49175         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49176         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49177         
49178         
49179         var html = '<iframe id="' + this.getId() +
49180             '___Frame" src="' + sLink +
49181             '" width="' + this.width +
49182             '" height="' + this.height + '"' +
49183             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49184             ' frameborder="0" scrolling="no"></iframe>' ;
49185
49186         return html ;
49187     },
49188     
49189     _insertHtmlBefore : function( html, element )
49190     {
49191         if ( element.insertAdjacentHTML )       {
49192             // IE
49193             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49194         } else { // Gecko
49195             var oRange = document.createRange() ;
49196             oRange.setStartBefore( element ) ;
49197             var oFragment = oRange.createContextualFragment( html );
49198             element.parentNode.insertBefore( oFragment, element ) ;
49199         }
49200     }
49201     
49202     
49203   
49204     
49205     
49206     
49207     
49208
49209 });
49210
49211 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49212
49213 function FCKeditor_OnComplete(editorInstance){
49214     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49215     f.fckEditor = editorInstance;
49216     //console.log("loaded");
49217     f.fireEvent('editorinit', f, editorInstance);
49218
49219   
49220
49221  
49222
49223
49224
49225
49226
49227
49228
49229
49230
49231
49232
49233
49234
49235
49236
49237 //<script type="text/javascript">
49238 /**
49239  * @class Roo.form.GridField
49240  * @extends Roo.form.Field
49241  * Embed a grid (or editable grid into a form)
49242  * STATUS ALPHA
49243  * 
49244  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49245  * it needs 
49246  * xgrid.store = Roo.data.Store
49247  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49248  * xgrid.store.reader = Roo.data.JsonReader 
49249  * 
49250  * 
49251  * @constructor
49252  * Creates a new GridField
49253  * @param {Object} config Configuration options
49254  */
49255 Roo.form.GridField = function(config){
49256     Roo.form.GridField.superclass.constructor.call(this, config);
49257      
49258 };
49259
49260 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49261     /**
49262      * @cfg {Number} width  - used to restrict width of grid..
49263      */
49264     width : 100,
49265     /**
49266      * @cfg {Number} height - used to restrict height of grid..
49267      */
49268     height : 50,
49269      /**
49270      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49271          * 
49272          *}
49273      */
49274     xgrid : false, 
49275     /**
49276      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49277      * {tag: "input", type: "checkbox", autocomplete: "off"})
49278      */
49279    // defaultAutoCreate : { tag: 'div' },
49280     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49281     /**
49282      * @cfg {String} addTitle Text to include for adding a title.
49283      */
49284     addTitle : false,
49285     //
49286     onResize : function(){
49287         Roo.form.Field.superclass.onResize.apply(this, arguments);
49288     },
49289
49290     initEvents : function(){
49291         // Roo.form.Checkbox.superclass.initEvents.call(this);
49292         // has no events...
49293        
49294     },
49295
49296
49297     getResizeEl : function(){
49298         return this.wrap;
49299     },
49300
49301     getPositionEl : function(){
49302         return this.wrap;
49303     },
49304
49305     // private
49306     onRender : function(ct, position){
49307         
49308         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49309         var style = this.style;
49310         delete this.style;
49311         
49312         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49313         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49314         this.viewEl = this.wrap.createChild({ tag: 'div' });
49315         if (style) {
49316             this.viewEl.applyStyles(style);
49317         }
49318         if (this.width) {
49319             this.viewEl.setWidth(this.width);
49320         }
49321         if (this.height) {
49322             this.viewEl.setHeight(this.height);
49323         }
49324         //if(this.inputValue !== undefined){
49325         //this.setValue(this.value);
49326         
49327         
49328         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49329         
49330         
49331         this.grid.render();
49332         this.grid.getDataSource().on('remove', this.refreshValue, this);
49333         this.grid.getDataSource().on('update', this.refreshValue, this);
49334         this.grid.on('afteredit', this.refreshValue, this);
49335  
49336     },
49337      
49338     
49339     /**
49340      * Sets the value of the item. 
49341      * @param {String} either an object  or a string..
49342      */
49343     setValue : function(v){
49344         //this.value = v;
49345         v = v || []; // empty set..
49346         // this does not seem smart - it really only affects memoryproxy grids..
49347         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49348             var ds = this.grid.getDataSource();
49349             // assumes a json reader..
49350             var data = {}
49351             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49352             ds.loadData( data);
49353         }
49354         // clear selection so it does not get stale.
49355         if (this.grid.sm) { 
49356             this.grid.sm.clearSelections();
49357         }
49358         
49359         Roo.form.GridField.superclass.setValue.call(this, v);
49360         this.refreshValue();
49361         // should load data in the grid really....
49362     },
49363     
49364     // private
49365     refreshValue: function() {
49366          var val = [];
49367         this.grid.getDataSource().each(function(r) {
49368             val.push(r.data);
49369         });
49370         this.el.dom.value = Roo.encode(val);
49371     }
49372     
49373      
49374     
49375     
49376 });/*
49377  * Based on:
49378  * Ext JS Library 1.1.1
49379  * Copyright(c) 2006-2007, Ext JS, LLC.
49380  *
49381  * Originally Released Under LGPL - original licence link has changed is not relivant.
49382  *
49383  * Fork - LGPL
49384  * <script type="text/javascript">
49385  */
49386 /**
49387  * @class Roo.form.DisplayField
49388  * @extends Roo.form.Field
49389  * A generic Field to display non-editable data.
49390  * @cfg {Boolean} closable (true|false) default false
49391  * @constructor
49392  * Creates a new Display Field item.
49393  * @param {Object} config Configuration options
49394  */
49395 Roo.form.DisplayField = function(config){
49396     Roo.form.DisplayField.superclass.constructor.call(this, config);
49397     
49398     this.addEvents({
49399         /**
49400          * @event close
49401          * Fires after the click the close btn
49402              * @param {Roo.form.DisplayField} this
49403              */
49404         close : true
49405     });
49406 };
49407
49408 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49409     inputType:      'hidden',
49410     allowBlank:     true,
49411     readOnly:         true,
49412     
49413  
49414     /**
49415      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49416      */
49417     focusClass : undefined,
49418     /**
49419      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49420      */
49421     fieldClass: 'x-form-field',
49422     
49423      /**
49424      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49425      */
49426     valueRenderer: undefined,
49427     
49428     width: 100,
49429     /**
49430      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49431      * {tag: "input", type: "checkbox", autocomplete: "off"})
49432      */
49433      
49434  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49435  
49436     closable : false,
49437     
49438     onResize : function(){
49439         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49440         
49441     },
49442
49443     initEvents : function(){
49444         // Roo.form.Checkbox.superclass.initEvents.call(this);
49445         // has no events...
49446         
49447         if(this.closable){
49448             this.closeEl.on('click', this.onClose, this);
49449         }
49450        
49451     },
49452
49453
49454     getResizeEl : function(){
49455         return this.wrap;
49456     },
49457
49458     getPositionEl : function(){
49459         return this.wrap;
49460     },
49461
49462     // private
49463     onRender : function(ct, position){
49464         
49465         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49466         //if(this.inputValue !== undefined){
49467         this.wrap = this.el.wrap();
49468         
49469         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49470         
49471         if(this.closable){
49472             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49473         }
49474         
49475         if (this.bodyStyle) {
49476             this.viewEl.applyStyles(this.bodyStyle);
49477         }
49478         //this.viewEl.setStyle('padding', '2px');
49479         
49480         this.setValue(this.value);
49481         
49482     },
49483 /*
49484     // private
49485     initValue : Roo.emptyFn,
49486
49487   */
49488
49489         // private
49490     onClick : function(){
49491         
49492     },
49493
49494     /**
49495      * Sets the checked state of the checkbox.
49496      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49497      */
49498     setValue : function(v){
49499         this.value = v;
49500         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49501         // this might be called before we have a dom element..
49502         if (!this.viewEl) {
49503             return;
49504         }
49505         this.viewEl.dom.innerHTML = html;
49506         Roo.form.DisplayField.superclass.setValue.call(this, v);
49507
49508     },
49509     
49510     onClose : function(e)
49511     {
49512         e.preventDefault();
49513         
49514         this.fireEvent('close', this);
49515     }
49516 });/*
49517  * 
49518  * Licence- LGPL
49519  * 
49520  */
49521
49522 /**
49523  * @class Roo.form.DayPicker
49524  * @extends Roo.form.Field
49525  * A Day picker show [M] [T] [W] ....
49526  * @constructor
49527  * Creates a new Day Picker
49528  * @param {Object} config Configuration options
49529  */
49530 Roo.form.DayPicker= function(config){
49531     Roo.form.DayPicker.superclass.constructor.call(this, config);
49532      
49533 };
49534
49535 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49536     /**
49537      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49538      */
49539     focusClass : undefined,
49540     /**
49541      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49542      */
49543     fieldClass: "x-form-field",
49544    
49545     /**
49546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49547      * {tag: "input", type: "checkbox", autocomplete: "off"})
49548      */
49549     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49550     
49551    
49552     actionMode : 'viewEl', 
49553     //
49554     // private
49555  
49556     inputType : 'hidden',
49557     
49558      
49559     inputElement: false, // real input element?
49560     basedOn: false, // ????
49561     
49562     isFormField: true, // not sure where this is needed!!!!
49563
49564     onResize : function(){
49565         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49566         if(!this.boxLabel){
49567             this.el.alignTo(this.wrap, 'c-c');
49568         }
49569     },
49570
49571     initEvents : function(){
49572         Roo.form.Checkbox.superclass.initEvents.call(this);
49573         this.el.on("click", this.onClick,  this);
49574         this.el.on("change", this.onClick,  this);
49575     },
49576
49577
49578     getResizeEl : function(){
49579         return this.wrap;
49580     },
49581
49582     getPositionEl : function(){
49583         return this.wrap;
49584     },
49585
49586     
49587     // private
49588     onRender : function(ct, position){
49589         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49590        
49591         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49592         
49593         var r1 = '<table><tr>';
49594         var r2 = '<tr class="x-form-daypick-icons">';
49595         for (var i=0; i < 7; i++) {
49596             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49597             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49598         }
49599         
49600         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49601         viewEl.select('img').on('click', this.onClick, this);
49602         this.viewEl = viewEl;   
49603         
49604         
49605         // this will not work on Chrome!!!
49606         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49607         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49608         
49609         
49610           
49611
49612     },
49613
49614     // private
49615     initValue : Roo.emptyFn,
49616
49617     /**
49618      * Returns the checked state of the checkbox.
49619      * @return {Boolean} True if checked, else false
49620      */
49621     getValue : function(){
49622         return this.el.dom.value;
49623         
49624     },
49625
49626         // private
49627     onClick : function(e){ 
49628         //this.setChecked(!this.checked);
49629         Roo.get(e.target).toggleClass('x-menu-item-checked');
49630         this.refreshValue();
49631         //if(this.el.dom.checked != this.checked){
49632         //    this.setValue(this.el.dom.checked);
49633        // }
49634     },
49635     
49636     // private
49637     refreshValue : function()
49638     {
49639         var val = '';
49640         this.viewEl.select('img',true).each(function(e,i,n)  {
49641             val += e.is(".x-menu-item-checked") ? String(n) : '';
49642         });
49643         this.setValue(val, true);
49644     },
49645
49646     /**
49647      * Sets the checked state of the checkbox.
49648      * On is always based on a string comparison between inputValue and the param.
49649      * @param {Boolean/String} value - the value to set 
49650      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49651      */
49652     setValue : function(v,suppressEvent){
49653         if (!this.el.dom) {
49654             return;
49655         }
49656         var old = this.el.dom.value ;
49657         this.el.dom.value = v;
49658         if (suppressEvent) {
49659             return ;
49660         }
49661          
49662         // update display..
49663         this.viewEl.select('img',true).each(function(e,i,n)  {
49664             
49665             var on = e.is(".x-menu-item-checked");
49666             var newv = v.indexOf(String(n)) > -1;
49667             if (on != newv) {
49668                 e.toggleClass('x-menu-item-checked');
49669             }
49670             
49671         });
49672         
49673         
49674         this.fireEvent('change', this, v, old);
49675         
49676         
49677     },
49678    
49679     // handle setting of hidden value by some other method!!?!?
49680     setFromHidden: function()
49681     {
49682         if(!this.el){
49683             return;
49684         }
49685         //console.log("SET FROM HIDDEN");
49686         //alert('setFrom hidden');
49687         this.setValue(this.el.dom.value);
49688     },
49689     
49690     onDestroy : function()
49691     {
49692         if(this.viewEl){
49693             Roo.get(this.viewEl).remove();
49694         }
49695          
49696         Roo.form.DayPicker.superclass.onDestroy.call(this);
49697     }
49698
49699 });/*
49700  * RooJS Library 1.1.1
49701  * Copyright(c) 2008-2011  Alan Knowles
49702  *
49703  * License - LGPL
49704  */
49705  
49706
49707 /**
49708  * @class Roo.form.ComboCheck
49709  * @extends Roo.form.ComboBox
49710  * A combobox for multiple select items.
49711  *
49712  * FIXME - could do with a reset button..
49713  * 
49714  * @constructor
49715  * Create a new ComboCheck
49716  * @param {Object} config Configuration options
49717  */
49718 Roo.form.ComboCheck = function(config){
49719     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49720     // should verify some data...
49721     // like
49722     // hiddenName = required..
49723     // displayField = required
49724     // valudField == required
49725     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49726     var _t = this;
49727     Roo.each(req, function(e) {
49728         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49729             throw "Roo.form.ComboCheck : missing value for: " + e;
49730         }
49731     });
49732     
49733     
49734 };
49735
49736 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49737      
49738      
49739     editable : false,
49740      
49741     selectedClass: 'x-menu-item-checked', 
49742     
49743     // private
49744     onRender : function(ct, position){
49745         var _t = this;
49746         
49747         
49748         
49749         if(!this.tpl){
49750             var cls = 'x-combo-list';
49751
49752             
49753             this.tpl =  new Roo.Template({
49754                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49755                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49756                    '<span>{' + this.displayField + '}</span>' +
49757                     '</div>' 
49758                 
49759             });
49760         }
49761  
49762         
49763         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49764         this.view.singleSelect = false;
49765         this.view.multiSelect = true;
49766         this.view.toggleSelect = true;
49767         this.pageTb.add(new Roo.Toolbar.Fill(), {
49768             
49769             text: 'Done',
49770             handler: function()
49771             {
49772                 _t.collapse();
49773             }
49774         });
49775     },
49776     
49777     onViewOver : function(e, t){
49778         // do nothing...
49779         return;
49780         
49781     },
49782     
49783     onViewClick : function(doFocus,index){
49784         return;
49785         
49786     },
49787     select: function () {
49788         //Roo.log("SELECT CALLED");
49789     },
49790      
49791     selectByValue : function(xv, scrollIntoView){
49792         var ar = this.getValueArray();
49793         var sels = [];
49794         
49795         Roo.each(ar, function(v) {
49796             if(v === undefined || v === null){
49797                 return;
49798             }
49799             var r = this.findRecord(this.valueField, v);
49800             if(r){
49801                 sels.push(this.store.indexOf(r))
49802                 
49803             }
49804         },this);
49805         this.view.select(sels);
49806         return false;
49807     },
49808     
49809     
49810     
49811     onSelect : function(record, index){
49812        // Roo.log("onselect Called");
49813        // this is only called by the clear button now..
49814         this.view.clearSelections();
49815         this.setValue('[]');
49816         if (this.value != this.valueBefore) {
49817             this.fireEvent('change', this, this.value, this.valueBefore);
49818             this.valueBefore = this.value;
49819         }
49820     },
49821     getValueArray : function()
49822     {
49823         var ar = [] ;
49824         
49825         try {
49826             //Roo.log(this.value);
49827             if (typeof(this.value) == 'undefined') {
49828                 return [];
49829             }
49830             var ar = Roo.decode(this.value);
49831             return  ar instanceof Array ? ar : []; //?? valid?
49832             
49833         } catch(e) {
49834             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49835             return [];
49836         }
49837          
49838     },
49839     expand : function ()
49840     {
49841         
49842         Roo.form.ComboCheck.superclass.expand.call(this);
49843         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49844         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49845         
49846
49847     },
49848     
49849     collapse : function(){
49850         Roo.form.ComboCheck.superclass.collapse.call(this);
49851         var sl = this.view.getSelectedIndexes();
49852         var st = this.store;
49853         var nv = [];
49854         var tv = [];
49855         var r;
49856         Roo.each(sl, function(i) {
49857             r = st.getAt(i);
49858             nv.push(r.get(this.valueField));
49859         },this);
49860         this.setValue(Roo.encode(nv));
49861         if (this.value != this.valueBefore) {
49862
49863             this.fireEvent('change', this, this.value, this.valueBefore);
49864             this.valueBefore = this.value;
49865         }
49866         
49867     },
49868     
49869     setValue : function(v){
49870         // Roo.log(v);
49871         this.value = v;
49872         
49873         var vals = this.getValueArray();
49874         var tv = [];
49875         Roo.each(vals, function(k) {
49876             var r = this.findRecord(this.valueField, k);
49877             if(r){
49878                 tv.push(r.data[this.displayField]);
49879             }else if(this.valueNotFoundText !== undefined){
49880                 tv.push( this.valueNotFoundText );
49881             }
49882         },this);
49883        // Roo.log(tv);
49884         
49885         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49886         this.hiddenField.value = v;
49887         this.value = v;
49888     }
49889     
49890 });/*
49891  * Based on:
49892  * Ext JS Library 1.1.1
49893  * Copyright(c) 2006-2007, Ext JS, LLC.
49894  *
49895  * Originally Released Under LGPL - original licence link has changed is not relivant.
49896  *
49897  * Fork - LGPL
49898  * <script type="text/javascript">
49899  */
49900  
49901 /**
49902  * @class Roo.form.Signature
49903  * @extends Roo.form.Field
49904  * Signature field.  
49905  * @constructor
49906  * 
49907  * @param {Object} config Configuration options
49908  */
49909
49910 Roo.form.Signature = function(config){
49911     Roo.form.Signature.superclass.constructor.call(this, config);
49912     
49913     this.addEvents({// not in used??
49914          /**
49915          * @event confirm
49916          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49917              * @param {Roo.form.Signature} combo This combo box
49918              */
49919         'confirm' : true,
49920         /**
49921          * @event reset
49922          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49923              * @param {Roo.form.ComboBox} combo This combo box
49924              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49925              */
49926         'reset' : true
49927     });
49928 };
49929
49930 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49931     /**
49932      * @cfg {Object} labels Label to use when rendering a form.
49933      * defaults to 
49934      * labels : { 
49935      *      clear : "Clear",
49936      *      confirm : "Confirm"
49937      *  }
49938      */
49939     labels : { 
49940         clear : "Clear",
49941         confirm : "Confirm"
49942     },
49943     /**
49944      * @cfg {Number} width The signature panel width (defaults to 300)
49945      */
49946     width: 300,
49947     /**
49948      * @cfg {Number} height The signature panel height (defaults to 100)
49949      */
49950     height : 100,
49951     /**
49952      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49953      */
49954     allowBlank : false,
49955     
49956     //private
49957     // {Object} signPanel The signature SVG panel element (defaults to {})
49958     signPanel : {},
49959     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49960     isMouseDown : false,
49961     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49962     isConfirmed : false,
49963     // {String} signatureTmp SVG mapping string (defaults to empty string)
49964     signatureTmp : '',
49965     
49966     
49967     defaultAutoCreate : { // modified by initCompnoent..
49968         tag: "input",
49969         type:"hidden"
49970     },
49971
49972     // private
49973     onRender : function(ct, position){
49974         
49975         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49976         
49977         this.wrap = this.el.wrap({
49978             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49979         });
49980         
49981         this.createToolbar(this);
49982         this.signPanel = this.wrap.createChild({
49983                 tag: 'div',
49984                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49985             }, this.el
49986         );
49987             
49988         this.svgID = Roo.id();
49989         this.svgEl = this.signPanel.createChild({
49990               xmlns : 'http://www.w3.org/2000/svg',
49991               tag : 'svg',
49992               id : this.svgID + "-svg",
49993               width: this.width,
49994               height: this.height,
49995               viewBox: '0 0 '+this.width+' '+this.height,
49996               cn : [
49997                 {
49998                     tag: "rect",
49999                     id: this.svgID + "-svg-r",
50000                     width: this.width,
50001                     height: this.height,
50002                     fill: "#ffa"
50003                 },
50004                 {
50005                     tag: "line",
50006                     id: this.svgID + "-svg-l",
50007                     x1: "0", // start
50008                     y1: (this.height*0.8), // start set the line in 80% of height
50009                     x2: this.width, // end
50010                     y2: (this.height*0.8), // end set the line in 80% of height
50011                     'stroke': "#666",
50012                     'stroke-width': "1",
50013                     'stroke-dasharray': "3",
50014                     'shape-rendering': "crispEdges",
50015                     'pointer-events': "none"
50016                 },
50017                 {
50018                     tag: "path",
50019                     id: this.svgID + "-svg-p",
50020                     'stroke': "navy",
50021                     'stroke-width': "3",
50022                     'fill': "none",
50023                     'pointer-events': 'none'
50024                 }
50025               ]
50026         });
50027         this.createSVG();
50028         this.svgBox = this.svgEl.dom.getScreenCTM();
50029     },
50030     createSVG : function(){ 
50031         var svg = this.signPanel;
50032         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50033         var t = this;
50034
50035         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50036         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50037         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50038         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50039         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50040         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50041         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50042         
50043     },
50044     isTouchEvent : function(e){
50045         return e.type.match(/^touch/);
50046     },
50047     getCoords : function (e) {
50048         var pt    = this.svgEl.dom.createSVGPoint();
50049         pt.x = e.clientX; 
50050         pt.y = e.clientY;
50051         if (this.isTouchEvent(e)) {
50052             pt.x =  e.targetTouches[0].clientX;
50053             pt.y = e.targetTouches[0].clientY;
50054         }
50055         var a = this.svgEl.dom.getScreenCTM();
50056         var b = a.inverse();
50057         var mx = pt.matrixTransform(b);
50058         return mx.x + ',' + mx.y;
50059     },
50060     //mouse event headler 
50061     down : function (e) {
50062         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50063         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50064         
50065         this.isMouseDown = true;
50066         
50067         e.preventDefault();
50068     },
50069     move : function (e) {
50070         if (this.isMouseDown) {
50071             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50072             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50073         }
50074         
50075         e.preventDefault();
50076     },
50077     up : function (e) {
50078         this.isMouseDown = false;
50079         var sp = this.signatureTmp.split(' ');
50080         
50081         if(sp.length > 1){
50082             if(!sp[sp.length-2].match(/^L/)){
50083                 sp.pop();
50084                 sp.pop();
50085                 sp.push("");
50086                 this.signatureTmp = sp.join(" ");
50087             }
50088         }
50089         if(this.getValue() != this.signatureTmp){
50090             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50091             this.isConfirmed = false;
50092         }
50093         e.preventDefault();
50094     },
50095     
50096     /**
50097      * Protected method that will not generally be called directly. It
50098      * is called when the editor creates its toolbar. Override this method if you need to
50099      * add custom toolbar buttons.
50100      * @param {HtmlEditor} editor
50101      */
50102     createToolbar : function(editor){
50103          function btn(id, toggle, handler){
50104             var xid = fid + '-'+ id ;
50105             return {
50106                 id : xid,
50107                 cmd : id,
50108                 cls : 'x-btn-icon x-edit-'+id,
50109                 enableToggle:toggle !== false,
50110                 scope: editor, // was editor...
50111                 handler:handler||editor.relayBtnCmd,
50112                 clickEvent:'mousedown',
50113                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50114                 tabIndex:-1
50115             };
50116         }
50117         
50118         
50119         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50120         this.tb = tb;
50121         this.tb.add(
50122            {
50123                 cls : ' x-signature-btn x-signature-'+id,
50124                 scope: editor, // was editor...
50125                 handler: this.reset,
50126                 clickEvent:'mousedown',
50127                 text: this.labels.clear
50128             },
50129             {
50130                  xtype : 'Fill',
50131                  xns: Roo.Toolbar
50132             }, 
50133             {
50134                 cls : '  x-signature-btn x-signature-'+id,
50135                 scope: editor, // was editor...
50136                 handler: this.confirmHandler,
50137                 clickEvent:'mousedown',
50138                 text: this.labels.confirm
50139             }
50140         );
50141     
50142     },
50143     //public
50144     /**
50145      * when user is clicked confirm then show this image.....
50146      * 
50147      * @return {String} Image Data URI
50148      */
50149     getImageDataURI : function(){
50150         var svg = this.svgEl.dom.parentNode.innerHTML;
50151         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50152         return src; 
50153     },
50154     /**
50155      * 
50156      * @return {Boolean} this.isConfirmed
50157      */
50158     getConfirmed : function(){
50159         return this.isConfirmed;
50160     },
50161     /**
50162      * 
50163      * @return {Number} this.width
50164      */
50165     getWidth : function(){
50166         return this.width;
50167     },
50168     /**
50169      * 
50170      * @return {Number} this.height
50171      */
50172     getHeight : function(){
50173         return this.height;
50174     },
50175     // private
50176     getSignature : function(){
50177         return this.signatureTmp;
50178     },
50179     // private
50180     reset : function(){
50181         this.signatureTmp = '';
50182         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50183         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50184         this.isConfirmed = false;
50185         Roo.form.Signature.superclass.reset.call(this);
50186     },
50187     setSignature : function(s){
50188         this.signatureTmp = s;
50189         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50190         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50191         this.setValue(s);
50192         this.isConfirmed = false;
50193         Roo.form.Signature.superclass.reset.call(this);
50194     }, 
50195     test : function(){
50196 //        Roo.log(this.signPanel.dom.contentWindow.up())
50197     },
50198     //private
50199     setConfirmed : function(){
50200         
50201         
50202         
50203 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50204     },
50205     // private
50206     confirmHandler : function(){
50207         if(!this.getSignature()){
50208             return;
50209         }
50210         
50211         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50212         this.setValue(this.getSignature());
50213         this.isConfirmed = true;
50214         
50215         this.fireEvent('confirm', this);
50216     },
50217     // private
50218     // Subclasses should provide the validation implementation by overriding this
50219     validateValue : function(value){
50220         if(this.allowBlank){
50221             return true;
50222         }
50223         
50224         if(this.isConfirmed){
50225             return true;
50226         }
50227         return false;
50228     }
50229 });/*
50230  * Based on:
50231  * Ext JS Library 1.1.1
50232  * Copyright(c) 2006-2007, Ext JS, LLC.
50233  *
50234  * Originally Released Under LGPL - original licence link has changed is not relivant.
50235  *
50236  * Fork - LGPL
50237  * <script type="text/javascript">
50238  */
50239  
50240
50241 /**
50242  * @class Roo.form.ComboBox
50243  * @extends Roo.form.TriggerField
50244  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50245  * @constructor
50246  * Create a new ComboBox.
50247  * @param {Object} config Configuration options
50248  */
50249 Roo.form.Select = function(config){
50250     Roo.form.Select.superclass.constructor.call(this, config);
50251      
50252 };
50253
50254 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50255     /**
50256      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50257      */
50258     /**
50259      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50260      * rendering into an Roo.Editor, defaults to false)
50261      */
50262     /**
50263      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50264      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50265      */
50266     /**
50267      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50268      */
50269     /**
50270      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50271      * the dropdown list (defaults to undefined, with no header element)
50272      */
50273
50274      /**
50275      * @cfg {String/Roo.Template} tpl The template to use to render the output
50276      */
50277      
50278     // private
50279     defaultAutoCreate : {tag: "select"  },
50280     /**
50281      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50282      */
50283     listWidth: undefined,
50284     /**
50285      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50286      * mode = 'remote' or 'text' if mode = 'local')
50287      */
50288     displayField: undefined,
50289     /**
50290      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50291      * mode = 'remote' or 'value' if mode = 'local'). 
50292      * Note: use of a valueField requires the user make a selection
50293      * in order for a value to be mapped.
50294      */
50295     valueField: undefined,
50296     
50297     
50298     /**
50299      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50300      * field's data value (defaults to the underlying DOM element's name)
50301      */
50302     hiddenName: undefined,
50303     /**
50304      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50305      */
50306     listClass: '',
50307     /**
50308      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50309      */
50310     selectedClass: 'x-combo-selected',
50311     /**
50312      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50313      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50314      * which displays a downward arrow icon).
50315      */
50316     triggerClass : 'x-form-arrow-trigger',
50317     /**
50318      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50319      */
50320     shadow:'sides',
50321     /**
50322      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50323      * anchor positions (defaults to 'tl-bl')
50324      */
50325     listAlign: 'tl-bl?',
50326     /**
50327      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50328      */
50329     maxHeight: 300,
50330     /**
50331      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50332      * query specified by the allQuery config option (defaults to 'query')
50333      */
50334     triggerAction: 'query',
50335     /**
50336      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50337      * (defaults to 4, does not apply if editable = false)
50338      */
50339     minChars : 4,
50340     /**
50341      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50342      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50343      */
50344     typeAhead: false,
50345     /**
50346      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50347      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50348      */
50349     queryDelay: 500,
50350     /**
50351      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50352      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50353      */
50354     pageSize: 0,
50355     /**
50356      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50357      * when editable = true (defaults to false)
50358      */
50359     selectOnFocus:false,
50360     /**
50361      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50362      */
50363     queryParam: 'query',
50364     /**
50365      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50366      * when mode = 'remote' (defaults to 'Loading...')
50367      */
50368     loadingText: 'Loading...',
50369     /**
50370      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50371      */
50372     resizable: false,
50373     /**
50374      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50375      */
50376     handleHeight : 8,
50377     /**
50378      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50379      * traditional select (defaults to true)
50380      */
50381     editable: true,
50382     /**
50383      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50384      */
50385     allQuery: '',
50386     /**
50387      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50388      */
50389     mode: 'remote',
50390     /**
50391      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50392      * listWidth has a higher value)
50393      */
50394     minListWidth : 70,
50395     /**
50396      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50397      * allow the user to set arbitrary text into the field (defaults to false)
50398      */
50399     forceSelection:false,
50400     /**
50401      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50402      * if typeAhead = true (defaults to 250)
50403      */
50404     typeAheadDelay : 250,
50405     /**
50406      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50407      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50408      */
50409     valueNotFoundText : undefined,
50410     
50411     /**
50412      * @cfg {String} defaultValue The value displayed after loading the store.
50413      */
50414     defaultValue: '',
50415     
50416     /**
50417      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50418      */
50419     blockFocus : false,
50420     
50421     /**
50422      * @cfg {Boolean} disableClear Disable showing of clear button.
50423      */
50424     disableClear : false,
50425     /**
50426      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50427      */
50428     alwaysQuery : false,
50429     
50430     //private
50431     addicon : false,
50432     editicon: false,
50433     
50434     // element that contains real text value.. (when hidden is used..)
50435      
50436     // private
50437     onRender : function(ct, position){
50438         Roo.form.Field.prototype.onRender.call(this, ct, position);
50439         
50440         if(this.store){
50441             this.store.on('beforeload', this.onBeforeLoad, this);
50442             this.store.on('load', this.onLoad, this);
50443             this.store.on('loadexception', this.onLoadException, this);
50444             this.store.load({});
50445         }
50446         
50447         
50448         
50449     },
50450
50451     // private
50452     initEvents : function(){
50453         //Roo.form.ComboBox.superclass.initEvents.call(this);
50454  
50455     },
50456
50457     onDestroy : function(){
50458        
50459         if(this.store){
50460             this.store.un('beforeload', this.onBeforeLoad, this);
50461             this.store.un('load', this.onLoad, this);
50462             this.store.un('loadexception', this.onLoadException, this);
50463         }
50464         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50465     },
50466
50467     // private
50468     fireKey : function(e){
50469         if(e.isNavKeyPress() && !this.list.isVisible()){
50470             this.fireEvent("specialkey", this, e);
50471         }
50472     },
50473
50474     // private
50475     onResize: function(w, h){
50476         
50477         return; 
50478     
50479         
50480     },
50481
50482     /**
50483      * Allow or prevent the user from directly editing the field text.  If false is passed,
50484      * the user will only be able to select from the items defined in the dropdown list.  This method
50485      * is the runtime equivalent of setting the 'editable' config option at config time.
50486      * @param {Boolean} value True to allow the user to directly edit the field text
50487      */
50488     setEditable : function(value){
50489          
50490     },
50491
50492     // private
50493     onBeforeLoad : function(){
50494         
50495         Roo.log("Select before load");
50496         return;
50497     
50498         this.innerList.update(this.loadingText ?
50499                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50500         //this.restrictHeight();
50501         this.selectedIndex = -1;
50502     },
50503
50504     // private
50505     onLoad : function(){
50506
50507     
50508         var dom = this.el.dom;
50509         dom.innerHTML = '';
50510          var od = dom.ownerDocument;
50511          
50512         if (this.emptyText) {
50513             var op = od.createElement('option');
50514             op.setAttribute('value', '');
50515             op.innerHTML = String.format('{0}', this.emptyText);
50516             dom.appendChild(op);
50517         }
50518         if(this.store.getCount() > 0){
50519            
50520             var vf = this.valueField;
50521             var df = this.displayField;
50522             this.store.data.each(function(r) {
50523                 // which colmsn to use... testing - cdoe / title..
50524                 var op = od.createElement('option');
50525                 op.setAttribute('value', r.data[vf]);
50526                 op.innerHTML = String.format('{0}', r.data[df]);
50527                 dom.appendChild(op);
50528             });
50529             if (typeof(this.defaultValue != 'undefined')) {
50530                 this.setValue(this.defaultValue);
50531             }
50532             
50533              
50534         }else{
50535             //this.onEmptyResults();
50536         }
50537         //this.el.focus();
50538     },
50539     // private
50540     onLoadException : function()
50541     {
50542         dom.innerHTML = '';
50543             
50544         Roo.log("Select on load exception");
50545         return;
50546     
50547         this.collapse();
50548         Roo.log(this.store.reader.jsonData);
50549         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50550             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50551         }
50552         
50553         
50554     },
50555     // private
50556     onTypeAhead : function(){
50557          
50558     },
50559
50560     // private
50561     onSelect : function(record, index){
50562         Roo.log('on select?');
50563         return;
50564         if(this.fireEvent('beforeselect', this, record, index) !== false){
50565             this.setFromData(index > -1 ? record.data : false);
50566             this.collapse();
50567             this.fireEvent('select', this, record, index);
50568         }
50569     },
50570
50571     /**
50572      * Returns the currently selected field value or empty string if no value is set.
50573      * @return {String} value The selected value
50574      */
50575     getValue : function(){
50576         var dom = this.el.dom;
50577         this.value = dom.options[dom.selectedIndex].value;
50578         return this.value;
50579         
50580     },
50581
50582     /**
50583      * Clears any text/value currently set in the field
50584      */
50585     clearValue : function(){
50586         this.value = '';
50587         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50588         
50589     },
50590
50591     /**
50592      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50593      * will be displayed in the field.  If the value does not match the data value of an existing item,
50594      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50595      * Otherwise the field will be blank (although the value will still be set).
50596      * @param {String} value The value to match
50597      */
50598     setValue : function(v){
50599         var d = this.el.dom;
50600         for (var i =0; i < d.options.length;i++) {
50601             if (v == d.options[i].value) {
50602                 d.selectedIndex = i;
50603                 this.value = v;
50604                 return;
50605             }
50606         }
50607         this.clearValue();
50608     },
50609     /**
50610      * @property {Object} the last set data for the element
50611      */
50612     
50613     lastData : false,
50614     /**
50615      * Sets the value of the field based on a object which is related to the record format for the store.
50616      * @param {Object} value the value to set as. or false on reset?
50617      */
50618     setFromData : function(o){
50619         Roo.log('setfrom data?');
50620          
50621         
50622         
50623     },
50624     // private
50625     reset : function(){
50626         this.clearValue();
50627     },
50628     // private
50629     findRecord : function(prop, value){
50630         
50631         return false;
50632     
50633         var record;
50634         if(this.store.getCount() > 0){
50635             this.store.each(function(r){
50636                 if(r.data[prop] == value){
50637                     record = r;
50638                     return false;
50639                 }
50640                 return true;
50641             });
50642         }
50643         return record;
50644     },
50645     
50646     getName: function()
50647     {
50648         // returns hidden if it's set..
50649         if (!this.rendered) {return ''};
50650         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50651         
50652     },
50653      
50654
50655     
50656
50657     // private
50658     onEmptyResults : function(){
50659         Roo.log('empty results');
50660         //this.collapse();
50661     },
50662
50663     /**
50664      * Returns true if the dropdown list is expanded, else false.
50665      */
50666     isExpanded : function(){
50667         return false;
50668     },
50669
50670     /**
50671      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50672      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50673      * @param {String} value The data value of the item to select
50674      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50675      * selected item if it is not currently in view (defaults to true)
50676      * @return {Boolean} True if the value matched an item in the list, else false
50677      */
50678     selectByValue : function(v, scrollIntoView){
50679         Roo.log('select By Value');
50680         return false;
50681     
50682         if(v !== undefined && v !== null){
50683             var r = this.findRecord(this.valueField || this.displayField, v);
50684             if(r){
50685                 this.select(this.store.indexOf(r), scrollIntoView);
50686                 return true;
50687             }
50688         }
50689         return false;
50690     },
50691
50692     /**
50693      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50694      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50695      * @param {Number} index The zero-based index of the list item to select
50696      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50697      * selected item if it is not currently in view (defaults to true)
50698      */
50699     select : function(index, scrollIntoView){
50700         Roo.log('select ');
50701         return  ;
50702         
50703         this.selectedIndex = index;
50704         this.view.select(index);
50705         if(scrollIntoView !== false){
50706             var el = this.view.getNode(index);
50707             if(el){
50708                 this.innerList.scrollChildIntoView(el, false);
50709             }
50710         }
50711     },
50712
50713       
50714
50715     // private
50716     validateBlur : function(){
50717         
50718         return;
50719         
50720     },
50721
50722     // private
50723     initQuery : function(){
50724         this.doQuery(this.getRawValue());
50725     },
50726
50727     // private
50728     doForce : function(){
50729         if(this.el.dom.value.length > 0){
50730             this.el.dom.value =
50731                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50732              
50733         }
50734     },
50735
50736     /**
50737      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50738      * query allowing the query action to be canceled if needed.
50739      * @param {String} query The SQL query to execute
50740      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50741      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50742      * saved in the current store (defaults to false)
50743      */
50744     doQuery : function(q, forceAll){
50745         
50746         Roo.log('doQuery?');
50747         if(q === undefined || q === null){
50748             q = '';
50749         }
50750         var qe = {
50751             query: q,
50752             forceAll: forceAll,
50753             combo: this,
50754             cancel:false
50755         };
50756         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50757             return false;
50758         }
50759         q = qe.query;
50760         forceAll = qe.forceAll;
50761         if(forceAll === true || (q.length >= this.minChars)){
50762             if(this.lastQuery != q || this.alwaysQuery){
50763                 this.lastQuery = q;
50764                 if(this.mode == 'local'){
50765                     this.selectedIndex = -1;
50766                     if(forceAll){
50767                         this.store.clearFilter();
50768                     }else{
50769                         this.store.filter(this.displayField, q);
50770                     }
50771                     this.onLoad();
50772                 }else{
50773                     this.store.baseParams[this.queryParam] = q;
50774                     this.store.load({
50775                         params: this.getParams(q)
50776                     });
50777                     this.expand();
50778                 }
50779             }else{
50780                 this.selectedIndex = -1;
50781                 this.onLoad();   
50782             }
50783         }
50784     },
50785
50786     // private
50787     getParams : function(q){
50788         var p = {};
50789         //p[this.queryParam] = q;
50790         if(this.pageSize){
50791             p.start = 0;
50792             p.limit = this.pageSize;
50793         }
50794         return p;
50795     },
50796
50797     /**
50798      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50799      */
50800     collapse : function(){
50801         
50802     },
50803
50804     // private
50805     collapseIf : function(e){
50806         
50807     },
50808
50809     /**
50810      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50811      */
50812     expand : function(){
50813         
50814     } ,
50815
50816     // private
50817      
50818
50819     /** 
50820     * @cfg {Boolean} grow 
50821     * @hide 
50822     */
50823     /** 
50824     * @cfg {Number} growMin 
50825     * @hide 
50826     */
50827     /** 
50828     * @cfg {Number} growMax 
50829     * @hide 
50830     */
50831     /**
50832      * @hide
50833      * @method autoSize
50834      */
50835     
50836     setWidth : function()
50837     {
50838         
50839     },
50840     getResizeEl : function(){
50841         return this.el;
50842     }
50843 });//<script type="text/javasscript">
50844  
50845
50846 /**
50847  * @class Roo.DDView
50848  * A DnD enabled version of Roo.View.
50849  * @param {Element/String} container The Element in which to create the View.
50850  * @param {String} tpl The template string used to create the markup for each element of the View
50851  * @param {Object} config The configuration properties. These include all the config options of
50852  * {@link Roo.View} plus some specific to this class.<br>
50853  * <p>
50854  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50855  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50856  * <p>
50857  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50858 .x-view-drag-insert-above {
50859         border-top:1px dotted #3366cc;
50860 }
50861 .x-view-drag-insert-below {
50862         border-bottom:1px dotted #3366cc;
50863 }
50864 </code></pre>
50865  * 
50866  */
50867  
50868 Roo.DDView = function(container, tpl, config) {
50869     Roo.DDView.superclass.constructor.apply(this, arguments);
50870     this.getEl().setStyle("outline", "0px none");
50871     this.getEl().unselectable();
50872     if (this.dragGroup) {
50873                 this.setDraggable(this.dragGroup.split(","));
50874     }
50875     if (this.dropGroup) {
50876                 this.setDroppable(this.dropGroup.split(","));
50877     }
50878     if (this.deletable) {
50879         this.setDeletable();
50880     }
50881     this.isDirtyFlag = false;
50882         this.addEvents({
50883                 "drop" : true
50884         });
50885 };
50886
50887 Roo.extend(Roo.DDView, Roo.View, {
50888 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50889 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50890 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50891 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50892
50893         isFormField: true,
50894
50895         reset: Roo.emptyFn,
50896         
50897         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50898
50899         validate: function() {
50900                 return true;
50901         },
50902         
50903         destroy: function() {
50904                 this.purgeListeners();
50905                 this.getEl.removeAllListeners();
50906                 this.getEl().remove();
50907                 if (this.dragZone) {
50908                         if (this.dragZone.destroy) {
50909                                 this.dragZone.destroy();
50910                         }
50911                 }
50912                 if (this.dropZone) {
50913                         if (this.dropZone.destroy) {
50914                                 this.dropZone.destroy();
50915                         }
50916                 }
50917         },
50918
50919 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50920         getName: function() {
50921                 return this.name;
50922         },
50923
50924 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50925         setValue: function(v) {
50926                 if (!this.store) {
50927                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50928                 }
50929                 var data = {};
50930                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50931                 this.store.proxy = new Roo.data.MemoryProxy(data);
50932                 this.store.load();
50933         },
50934
50935 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50936         getValue: function() {
50937                 var result = '(';
50938                 this.store.each(function(rec) {
50939                         result += rec.id + ',';
50940                 });
50941                 return result.substr(0, result.length - 1) + ')';
50942         },
50943         
50944         getIds: function() {
50945                 var i = 0, result = new Array(this.store.getCount());
50946                 this.store.each(function(rec) {
50947                         result[i++] = rec.id;
50948                 });
50949                 return result;
50950         },
50951         
50952         isDirty: function() {
50953                 return this.isDirtyFlag;
50954         },
50955
50956 /**
50957  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50958  *      whole Element becomes the target, and this causes the drop gesture to append.
50959  */
50960     getTargetFromEvent : function(e) {
50961                 var target = e.getTarget();
50962                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50963                 target = target.parentNode;
50964                 }
50965                 if (!target) {
50966                         target = this.el.dom.lastChild || this.el.dom;
50967                 }
50968                 return target;
50969     },
50970
50971 /**
50972  *      Create the drag data which consists of an object which has the property "ddel" as
50973  *      the drag proxy element. 
50974  */
50975     getDragData : function(e) {
50976         var target = this.findItemFromChild(e.getTarget());
50977                 if(target) {
50978                         this.handleSelection(e);
50979                         var selNodes = this.getSelectedNodes();
50980             var dragData = {
50981                 source: this,
50982                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50983                 nodes: selNodes,
50984                 records: []
50985                         };
50986                         var selectedIndices = this.getSelectedIndexes();
50987                         for (var i = 0; i < selectedIndices.length; i++) {
50988                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50989                         }
50990                         if (selNodes.length == 1) {
50991                                 dragData.ddel = target.cloneNode(true); // the div element
50992                         } else {
50993                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50994                                 div.className = 'multi-proxy';
50995                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50996                                         div.appendChild(selNodes[i].cloneNode(true));
50997                                 }
50998                                 dragData.ddel = div;
50999                         }
51000             //console.log(dragData)
51001             //console.log(dragData.ddel.innerHTML)
51002                         return dragData;
51003                 }
51004         //console.log('nodragData')
51005                 return false;
51006     },
51007     
51008 /**     Specify to which ddGroup items in this DDView may be dragged. */
51009     setDraggable: function(ddGroup) {
51010         if (ddGroup instanceof Array) {
51011                 Roo.each(ddGroup, this.setDraggable, this);
51012                 return;
51013         }
51014         if (this.dragZone) {
51015                 this.dragZone.addToGroup(ddGroup);
51016         } else {
51017                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51018                                 containerScroll: true,
51019                                 ddGroup: ddGroup 
51020
51021                         });
51022 //                      Draggability implies selection. DragZone's mousedown selects the element.
51023                         if (!this.multiSelect) { this.singleSelect = true; }
51024
51025 //                      Wire the DragZone's handlers up to methods in *this*
51026                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51027                 }
51028     },
51029
51030 /**     Specify from which ddGroup this DDView accepts drops. */
51031     setDroppable: function(ddGroup) {
51032         if (ddGroup instanceof Array) {
51033                 Roo.each(ddGroup, this.setDroppable, this);
51034                 return;
51035         }
51036         if (this.dropZone) {
51037                 this.dropZone.addToGroup(ddGroup);
51038         } else {
51039                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51040                                 containerScroll: true,
51041                                 ddGroup: ddGroup
51042                         });
51043
51044 //                      Wire the DropZone's handlers up to methods in *this*
51045                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51046                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51047                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51048                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51049                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51050                 }
51051     },
51052
51053 /**     Decide whether to drop above or below a View node. */
51054     getDropPoint : function(e, n, dd){
51055         if (n == this.el.dom) { return "above"; }
51056                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51057                 var c = t + (b - t) / 2;
51058                 var y = Roo.lib.Event.getPageY(e);
51059                 if(y <= c) {
51060                         return "above";
51061                 }else{
51062                         return "below";
51063                 }
51064     },
51065
51066     onNodeEnter : function(n, dd, e, data){
51067                 return false;
51068     },
51069     
51070     onNodeOver : function(n, dd, e, data){
51071                 var pt = this.getDropPoint(e, n, dd);
51072                 // set the insert point style on the target node
51073                 var dragElClass = this.dropNotAllowed;
51074                 if (pt) {
51075                         var targetElClass;
51076                         if (pt == "above"){
51077                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51078                                 targetElClass = "x-view-drag-insert-above";
51079                         } else {
51080                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51081                                 targetElClass = "x-view-drag-insert-below";
51082                         }
51083                         if (this.lastInsertClass != targetElClass){
51084                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51085                                 this.lastInsertClass = targetElClass;
51086                         }
51087                 }
51088                 return dragElClass;
51089         },
51090
51091     onNodeOut : function(n, dd, e, data){
51092                 this.removeDropIndicators(n);
51093     },
51094
51095     onNodeDrop : function(n, dd, e, data){
51096         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51097                 return false;
51098         }
51099         var pt = this.getDropPoint(e, n, dd);
51100                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51101                 if (pt == "below") { insertAt++; }
51102                 for (var i = 0; i < data.records.length; i++) {
51103                         var r = data.records[i];
51104                         var dup = this.store.getById(r.id);
51105                         if (dup && (dd != this.dragZone)) {
51106                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51107                         } else {
51108                                 if (data.copy) {
51109                                         this.store.insert(insertAt++, r.copy());
51110                                 } else {
51111                                         data.source.isDirtyFlag = true;
51112                                         r.store.remove(r);
51113                                         this.store.insert(insertAt++, r);
51114                                 }
51115                                 this.isDirtyFlag = true;
51116                         }
51117                 }
51118                 this.dragZone.cachedTarget = null;
51119                 return true;
51120     },
51121
51122     removeDropIndicators : function(n){
51123                 if(n){
51124                         Roo.fly(n).removeClass([
51125                                 "x-view-drag-insert-above",
51126                                 "x-view-drag-insert-below"]);
51127                         this.lastInsertClass = "_noclass";
51128                 }
51129     },
51130
51131 /**
51132  *      Utility method. Add a delete option to the DDView's context menu.
51133  *      @param {String} imageUrl The URL of the "delete" icon image.
51134  */
51135         setDeletable: function(imageUrl) {
51136                 if (!this.singleSelect && !this.multiSelect) {
51137                         this.singleSelect = true;
51138                 }
51139                 var c = this.getContextMenu();
51140                 this.contextMenu.on("itemclick", function(item) {
51141                         switch (item.id) {
51142                                 case "delete":
51143                                         this.remove(this.getSelectedIndexes());
51144                                         break;
51145                         }
51146                 }, this);
51147                 this.contextMenu.add({
51148                         icon: imageUrl,
51149                         id: "delete",
51150                         text: 'Delete'
51151                 });
51152         },
51153         
51154 /**     Return the context menu for this DDView. */
51155         getContextMenu: function() {
51156                 if (!this.contextMenu) {
51157 //                      Create the View's context menu
51158                         this.contextMenu = new Roo.menu.Menu({
51159                                 id: this.id + "-contextmenu"
51160                         });
51161                         this.el.on("contextmenu", this.showContextMenu, this);
51162                 }
51163                 return this.contextMenu;
51164         },
51165         
51166         disableContextMenu: function() {
51167                 if (this.contextMenu) {
51168                         this.el.un("contextmenu", this.showContextMenu, this);
51169                 }
51170         },
51171
51172         showContextMenu: function(e, item) {
51173         item = this.findItemFromChild(e.getTarget());
51174                 if (item) {
51175                         e.stopEvent();
51176                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51177                         this.contextMenu.showAt(e.getXY());
51178             }
51179     },
51180
51181 /**
51182  *      Remove {@link Roo.data.Record}s at the specified indices.
51183  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51184  */
51185     remove: function(selectedIndices) {
51186                 selectedIndices = [].concat(selectedIndices);
51187                 for (var i = 0; i < selectedIndices.length; i++) {
51188                         var rec = this.store.getAt(selectedIndices[i]);
51189                         this.store.remove(rec);
51190                 }
51191     },
51192
51193 /**
51194  *      Double click fires the event, but also, if this is draggable, and there is only one other
51195  *      related DropZone, it transfers the selected node.
51196  */
51197     onDblClick : function(e){
51198         var item = this.findItemFromChild(e.getTarget());
51199         if(item){
51200             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51201                 return false;
51202             }
51203             if (this.dragGroup) {
51204                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51205                     while (targets.indexOf(this.dropZone) > -1) {
51206                             targets.remove(this.dropZone);
51207                                 }
51208                     if (targets.length == 1) {
51209                                         this.dragZone.cachedTarget = null;
51210                         var el = Roo.get(targets[0].getEl());
51211                         var box = el.getBox(true);
51212                         targets[0].onNodeDrop(el.dom, {
51213                                 target: el.dom,
51214                                 xy: [box.x, box.y + box.height - 1]
51215                         }, null, this.getDragData(e));
51216                     }
51217                 }
51218         }
51219     },
51220     
51221     handleSelection: function(e) {
51222                 this.dragZone.cachedTarget = null;
51223         var item = this.findItemFromChild(e.getTarget());
51224         if (!item) {
51225                 this.clearSelections(true);
51226                 return;
51227         }
51228                 if (item && (this.multiSelect || this.singleSelect)){
51229                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51230                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51231                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51232                                 this.unselect(item);
51233                         } else {
51234                                 this.select(item, this.multiSelect && e.ctrlKey);
51235                                 this.lastSelection = item;
51236                         }
51237                 }
51238     },
51239
51240     onItemClick : function(item, index, e){
51241                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51242                         return false;
51243                 }
51244                 return true;
51245     },
51246
51247     unselect : function(nodeInfo, suppressEvent){
51248                 var node = this.getNode(nodeInfo);
51249                 if(node && this.isSelected(node)){
51250                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51251                                 Roo.fly(node).removeClass(this.selectedClass);
51252                                 this.selections.remove(node);
51253                                 if(!suppressEvent){
51254                                         this.fireEvent("selectionchange", this, this.selections);
51255                                 }
51256                         }
51257                 }
51258     }
51259 });
51260 /*
51261  * Based on:
51262  * Ext JS Library 1.1.1
51263  * Copyright(c) 2006-2007, Ext JS, LLC.
51264  *
51265  * Originally Released Under LGPL - original licence link has changed is not relivant.
51266  *
51267  * Fork - LGPL
51268  * <script type="text/javascript">
51269  */
51270  
51271 /**
51272  * @class Roo.LayoutManager
51273  * @extends Roo.util.Observable
51274  * Base class for layout managers.
51275  */
51276 Roo.LayoutManager = function(container, config){
51277     Roo.LayoutManager.superclass.constructor.call(this);
51278     this.el = Roo.get(container);
51279     // ie scrollbar fix
51280     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51281         document.body.scroll = "no";
51282     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51283         this.el.position('relative');
51284     }
51285     this.id = this.el.id;
51286     this.el.addClass("x-layout-container");
51287     /** false to disable window resize monitoring @type Boolean */
51288     this.monitorWindowResize = true;
51289     this.regions = {};
51290     this.addEvents({
51291         /**
51292          * @event layout
51293          * Fires when a layout is performed. 
51294          * @param {Roo.LayoutManager} this
51295          */
51296         "layout" : true,
51297         /**
51298          * @event regionresized
51299          * Fires when the user resizes a region. 
51300          * @param {Roo.LayoutRegion} region The resized region
51301          * @param {Number} newSize The new size (width for east/west, height for north/south)
51302          */
51303         "regionresized" : true,
51304         /**
51305          * @event regioncollapsed
51306          * Fires when a region is collapsed. 
51307          * @param {Roo.LayoutRegion} region The collapsed region
51308          */
51309         "regioncollapsed" : true,
51310         /**
51311          * @event regionexpanded
51312          * Fires when a region is expanded.  
51313          * @param {Roo.LayoutRegion} region The expanded region
51314          */
51315         "regionexpanded" : true
51316     });
51317     this.updating = false;
51318     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51319 };
51320
51321 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51322     /**
51323      * Returns true if this layout is currently being updated
51324      * @return {Boolean}
51325      */
51326     isUpdating : function(){
51327         return this.updating; 
51328     },
51329     
51330     /**
51331      * Suspend the LayoutManager from doing auto-layouts while
51332      * making multiple add or remove calls
51333      */
51334     beginUpdate : function(){
51335         this.updating = true;    
51336     },
51337     
51338     /**
51339      * Restore auto-layouts and optionally disable the manager from performing a layout
51340      * @param {Boolean} noLayout true to disable a layout update 
51341      */
51342     endUpdate : function(noLayout){
51343         this.updating = false;
51344         if(!noLayout){
51345             this.layout();
51346         }    
51347     },
51348     
51349     layout: function(){
51350         
51351     },
51352     
51353     onRegionResized : function(region, newSize){
51354         this.fireEvent("regionresized", region, newSize);
51355         this.layout();
51356     },
51357     
51358     onRegionCollapsed : function(region){
51359         this.fireEvent("regioncollapsed", region);
51360     },
51361     
51362     onRegionExpanded : function(region){
51363         this.fireEvent("regionexpanded", region);
51364     },
51365         
51366     /**
51367      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51368      * performs box-model adjustments.
51369      * @return {Object} The size as an object {width: (the width), height: (the height)}
51370      */
51371     getViewSize : function(){
51372         var size;
51373         if(this.el.dom != document.body){
51374             size = this.el.getSize();
51375         }else{
51376             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51377         }
51378         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51379         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51380         return size;
51381     },
51382     
51383     /**
51384      * Returns the Element this layout is bound to.
51385      * @return {Roo.Element}
51386      */
51387     getEl : function(){
51388         return this.el;
51389     },
51390     
51391     /**
51392      * Returns the specified region.
51393      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51394      * @return {Roo.LayoutRegion}
51395      */
51396     getRegion : function(target){
51397         return this.regions[target.toLowerCase()];
51398     },
51399     
51400     onWindowResize : function(){
51401         if(this.monitorWindowResize){
51402             this.layout();
51403         }
51404     }
51405 });/*
51406  * Based on:
51407  * Ext JS Library 1.1.1
51408  * Copyright(c) 2006-2007, Ext JS, LLC.
51409  *
51410  * Originally Released Under LGPL - original licence link has changed is not relivant.
51411  *
51412  * Fork - LGPL
51413  * <script type="text/javascript">
51414  */
51415 /**
51416  * @class Roo.BorderLayout
51417  * @extends Roo.LayoutManager
51418  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51419  * please see: <br><br>
51420  * <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>
51421  * <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>
51422  * Example:
51423  <pre><code>
51424  var layout = new Roo.BorderLayout(document.body, {
51425     north: {
51426         initialSize: 25,
51427         titlebar: false
51428     },
51429     west: {
51430         split:true,
51431         initialSize: 200,
51432         minSize: 175,
51433         maxSize: 400,
51434         titlebar: true,
51435         collapsible: true
51436     },
51437     east: {
51438         split:true,
51439         initialSize: 202,
51440         minSize: 175,
51441         maxSize: 400,
51442         titlebar: true,
51443         collapsible: true
51444     },
51445     south: {
51446         split:true,
51447         initialSize: 100,
51448         minSize: 100,
51449         maxSize: 200,
51450         titlebar: true,
51451         collapsible: true
51452     },
51453     center: {
51454         titlebar: true,
51455         autoScroll:true,
51456         resizeTabs: true,
51457         minTabWidth: 50,
51458         preferredTabWidth: 150
51459     }
51460 });
51461
51462 // shorthand
51463 var CP = Roo.ContentPanel;
51464
51465 layout.beginUpdate();
51466 layout.add("north", new CP("north", "North"));
51467 layout.add("south", new CP("south", {title: "South", closable: true}));
51468 layout.add("west", new CP("west", {title: "West"}));
51469 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51470 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51471 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51472 layout.getRegion("center").showPanel("center1");
51473 layout.endUpdate();
51474 </code></pre>
51475
51476 <b>The container the layout is rendered into can be either the body element or any other element.
51477 If it is not the body element, the container needs to either be an absolute positioned element,
51478 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51479 the container size if it is not the body element.</b>
51480
51481 * @constructor
51482 * Create a new BorderLayout
51483 * @param {String/HTMLElement/Element} container The container this layout is bound to
51484 * @param {Object} config Configuration options
51485  */
51486 Roo.BorderLayout = function(container, config){
51487     config = config || {};
51488     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51489     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51490     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51491         var target = this.factory.validRegions[i];
51492         if(config[target]){
51493             this.addRegion(target, config[target]);
51494         }
51495     }
51496 };
51497
51498 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51499     /**
51500      * Creates and adds a new region if it doesn't already exist.
51501      * @param {String} target The target region key (north, south, east, west or center).
51502      * @param {Object} config The regions config object
51503      * @return {BorderLayoutRegion} The new region
51504      */
51505     addRegion : function(target, config){
51506         if(!this.regions[target]){
51507             var r = this.factory.create(target, this, config);
51508             this.bindRegion(target, r);
51509         }
51510         return this.regions[target];
51511     },
51512
51513     // private (kinda)
51514     bindRegion : function(name, r){
51515         this.regions[name] = r;
51516         r.on("visibilitychange", this.layout, this);
51517         r.on("paneladded", this.layout, this);
51518         r.on("panelremoved", this.layout, this);
51519         r.on("invalidated", this.layout, this);
51520         r.on("resized", this.onRegionResized, this);
51521         r.on("collapsed", this.onRegionCollapsed, this);
51522         r.on("expanded", this.onRegionExpanded, this);
51523     },
51524
51525     /**
51526      * Performs a layout update.
51527      */
51528     layout : function(){
51529         if(this.updating) {
51530             return;
51531         }
51532         var size = this.getViewSize();
51533         var w = size.width;
51534         var h = size.height;
51535         var centerW = w;
51536         var centerH = h;
51537         var centerY = 0;
51538         var centerX = 0;
51539         //var x = 0, y = 0;
51540
51541         var rs = this.regions;
51542         var north = rs["north"];
51543         var south = rs["south"]; 
51544         var west = rs["west"];
51545         var east = rs["east"];
51546         var center = rs["center"];
51547         //if(this.hideOnLayout){ // not supported anymore
51548             //c.el.setStyle("display", "none");
51549         //}
51550         if(north && north.isVisible()){
51551             var b = north.getBox();
51552             var m = north.getMargins();
51553             b.width = w - (m.left+m.right);
51554             b.x = m.left;
51555             b.y = m.top;
51556             centerY = b.height + b.y + m.bottom;
51557             centerH -= centerY;
51558             north.updateBox(this.safeBox(b));
51559         }
51560         if(south && south.isVisible()){
51561             var b = south.getBox();
51562             var m = south.getMargins();
51563             b.width = w - (m.left+m.right);
51564             b.x = m.left;
51565             var totalHeight = (b.height + m.top + m.bottom);
51566             b.y = h - totalHeight + m.top;
51567             centerH -= totalHeight;
51568             south.updateBox(this.safeBox(b));
51569         }
51570         if(west && west.isVisible()){
51571             var b = west.getBox();
51572             var m = west.getMargins();
51573             b.height = centerH - (m.top+m.bottom);
51574             b.x = m.left;
51575             b.y = centerY + m.top;
51576             var totalWidth = (b.width + m.left + m.right);
51577             centerX += totalWidth;
51578             centerW -= totalWidth;
51579             west.updateBox(this.safeBox(b));
51580         }
51581         if(east && east.isVisible()){
51582             var b = east.getBox();
51583             var m = east.getMargins();
51584             b.height = centerH - (m.top+m.bottom);
51585             var totalWidth = (b.width + m.left + m.right);
51586             b.x = w - totalWidth + m.left;
51587             b.y = centerY + m.top;
51588             centerW -= totalWidth;
51589             east.updateBox(this.safeBox(b));
51590         }
51591         if(center){
51592             var m = center.getMargins();
51593             var centerBox = {
51594                 x: centerX + m.left,
51595                 y: centerY + m.top,
51596                 width: centerW - (m.left+m.right),
51597                 height: centerH - (m.top+m.bottom)
51598             };
51599             //if(this.hideOnLayout){
51600                 //center.el.setStyle("display", "block");
51601             //}
51602             center.updateBox(this.safeBox(centerBox));
51603         }
51604         this.el.repaint();
51605         this.fireEvent("layout", this);
51606     },
51607
51608     // private
51609     safeBox : function(box){
51610         box.width = Math.max(0, box.width);
51611         box.height = Math.max(0, box.height);
51612         return box;
51613     },
51614
51615     /**
51616      * Adds a ContentPanel (or subclass) to this layout.
51617      * @param {String} target The target region key (north, south, east, west or center).
51618      * @param {Roo.ContentPanel} panel The panel to add
51619      * @return {Roo.ContentPanel} The added panel
51620      */
51621     add : function(target, panel){
51622          
51623         target = target.toLowerCase();
51624         return this.regions[target].add(panel);
51625     },
51626
51627     /**
51628      * Remove a ContentPanel (or subclass) to this layout.
51629      * @param {String} target The target region key (north, south, east, west or center).
51630      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51631      * @return {Roo.ContentPanel} The removed panel
51632      */
51633     remove : function(target, panel){
51634         target = target.toLowerCase();
51635         return this.regions[target].remove(panel);
51636     },
51637
51638     /**
51639      * Searches all regions for a panel with the specified id
51640      * @param {String} panelId
51641      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51642      */
51643     findPanel : function(panelId){
51644         var rs = this.regions;
51645         for(var target in rs){
51646             if(typeof rs[target] != "function"){
51647                 var p = rs[target].getPanel(panelId);
51648                 if(p){
51649                     return p;
51650                 }
51651             }
51652         }
51653         return null;
51654     },
51655
51656     /**
51657      * Searches all regions for a panel with the specified id and activates (shows) it.
51658      * @param {String/ContentPanel} panelId The panels id or the panel itself
51659      * @return {Roo.ContentPanel} The shown panel or null
51660      */
51661     showPanel : function(panelId) {
51662       var rs = this.regions;
51663       for(var target in rs){
51664          var r = rs[target];
51665          if(typeof r != "function"){
51666             if(r.hasPanel(panelId)){
51667                return r.showPanel(panelId);
51668             }
51669          }
51670       }
51671       return null;
51672    },
51673
51674    /**
51675      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51676      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51677      */
51678     restoreState : function(provider){
51679         if(!provider){
51680             provider = Roo.state.Manager;
51681         }
51682         var sm = new Roo.LayoutStateManager();
51683         sm.init(this, provider);
51684     },
51685
51686     /**
51687      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51688      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51689      * a valid ContentPanel config object.  Example:
51690      * <pre><code>
51691 // Create the main layout
51692 var layout = new Roo.BorderLayout('main-ct', {
51693     west: {
51694         split:true,
51695         minSize: 175,
51696         titlebar: true
51697     },
51698     center: {
51699         title:'Components'
51700     }
51701 }, 'main-ct');
51702
51703 // Create and add multiple ContentPanels at once via configs
51704 layout.batchAdd({
51705    west: {
51706        id: 'source-files',
51707        autoCreate:true,
51708        title:'Ext Source Files',
51709        autoScroll:true,
51710        fitToFrame:true
51711    },
51712    center : {
51713        el: cview,
51714        autoScroll:true,
51715        fitToFrame:true,
51716        toolbar: tb,
51717        resizeEl:'cbody'
51718    }
51719 });
51720 </code></pre>
51721      * @param {Object} regions An object containing ContentPanel configs by region name
51722      */
51723     batchAdd : function(regions){
51724         this.beginUpdate();
51725         for(var rname in regions){
51726             var lr = this.regions[rname];
51727             if(lr){
51728                 this.addTypedPanels(lr, regions[rname]);
51729             }
51730         }
51731         this.endUpdate();
51732     },
51733
51734     // private
51735     addTypedPanels : function(lr, ps){
51736         if(typeof ps == 'string'){
51737             lr.add(new Roo.ContentPanel(ps));
51738         }
51739         else if(ps instanceof Array){
51740             for(var i =0, len = ps.length; i < len; i++){
51741                 this.addTypedPanels(lr, ps[i]);
51742             }
51743         }
51744         else if(!ps.events){ // raw config?
51745             var el = ps.el;
51746             delete ps.el; // prevent conflict
51747             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51748         }
51749         else {  // panel object assumed!
51750             lr.add(ps);
51751         }
51752     },
51753     /**
51754      * Adds a xtype elements to the layout.
51755      * <pre><code>
51756
51757 layout.addxtype({
51758        xtype : 'ContentPanel',
51759        region: 'west',
51760        items: [ .... ]
51761    }
51762 );
51763
51764 layout.addxtype({
51765         xtype : 'NestedLayoutPanel',
51766         region: 'west',
51767         layout: {
51768            center: { },
51769            west: { }   
51770         },
51771         items : [ ... list of content panels or nested layout panels.. ]
51772    }
51773 );
51774 </code></pre>
51775      * @param {Object} cfg Xtype definition of item to add.
51776      */
51777     addxtype : function(cfg)
51778     {
51779         // basically accepts a pannel...
51780         // can accept a layout region..!?!?
51781         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51782         
51783         if (!cfg.xtype.match(/Panel$/)) {
51784             return false;
51785         }
51786         var ret = false;
51787         
51788         if (typeof(cfg.region) == 'undefined') {
51789             Roo.log("Failed to add Panel, region was not set");
51790             Roo.log(cfg);
51791             return false;
51792         }
51793         var region = cfg.region;
51794         delete cfg.region;
51795         
51796           
51797         var xitems = [];
51798         if (cfg.items) {
51799             xitems = cfg.items;
51800             delete cfg.items;
51801         }
51802         var nb = false;
51803         
51804         switch(cfg.xtype) 
51805         {
51806             case 'ContentPanel':  // ContentPanel (el, cfg)
51807             case 'ScrollPanel':  // ContentPanel (el, cfg)
51808             case 'ViewPanel': 
51809                 if(cfg.autoCreate) {
51810                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51811                 } else {
51812                     var el = this.el.createChild();
51813                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51814                 }
51815                 
51816                 this.add(region, ret);
51817                 break;
51818             
51819             
51820             case 'TreePanel': // our new panel!
51821                 cfg.el = this.el.createChild();
51822                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51823                 this.add(region, ret);
51824                 break;
51825             
51826             case 'NestedLayoutPanel': 
51827                 // create a new Layout (which is  a Border Layout...
51828                 var el = this.el.createChild();
51829                 var clayout = cfg.layout;
51830                 delete cfg.layout;
51831                 clayout.items   = clayout.items  || [];
51832                 // replace this exitems with the clayout ones..
51833                 xitems = clayout.items;
51834                  
51835                 
51836                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51837                     cfg.background = false;
51838                 }
51839                 var layout = new Roo.BorderLayout(el, clayout);
51840                 
51841                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51842                 //console.log('adding nested layout panel '  + cfg.toSource());
51843                 this.add(region, ret);
51844                 nb = {}; /// find first...
51845                 break;
51846                 
51847             case 'GridPanel': 
51848             
51849                 // needs grid and region
51850                 
51851                 //var el = this.getRegion(region).el.createChild();
51852                 var el = this.el.createChild();
51853                 // create the grid first...
51854                 
51855                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51856                 delete cfg.grid;
51857                 if (region == 'center' && this.active ) {
51858                     cfg.background = false;
51859                 }
51860                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51861                 
51862                 this.add(region, ret);
51863                 if (cfg.background) {
51864                     ret.on('activate', function(gp) {
51865                         if (!gp.grid.rendered) {
51866                             gp.grid.render();
51867                         }
51868                     });
51869                 } else {
51870                     grid.render();
51871                 }
51872                 break;
51873            
51874            
51875            
51876                 
51877                 
51878                 
51879             default:
51880                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51881                     
51882                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51883                     this.add(region, ret);
51884                 } else {
51885                 
51886                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51887                     return null;
51888                 }
51889                 
51890              // GridPanel (grid, cfg)
51891             
51892         }
51893         this.beginUpdate();
51894         // add children..
51895         var region = '';
51896         var abn = {};
51897         Roo.each(xitems, function(i)  {
51898             region = nb && i.region ? i.region : false;
51899             
51900             var add = ret.addxtype(i);
51901            
51902             if (region) {
51903                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51904                 if (!i.background) {
51905                     abn[region] = nb[region] ;
51906                 }
51907             }
51908             
51909         });
51910         this.endUpdate();
51911
51912         // make the last non-background panel active..
51913         //if (nb) { Roo.log(abn); }
51914         if (nb) {
51915             
51916             for(var r in abn) {
51917                 region = this.getRegion(r);
51918                 if (region) {
51919                     // tried using nb[r], but it does not work..
51920                      
51921                     region.showPanel(abn[r]);
51922                    
51923                 }
51924             }
51925         }
51926         return ret;
51927         
51928     }
51929 });
51930
51931 /**
51932  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51933  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51934  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51935  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51936  * <pre><code>
51937 // shorthand
51938 var CP = Roo.ContentPanel;
51939
51940 var layout = Roo.BorderLayout.create({
51941     north: {
51942         initialSize: 25,
51943         titlebar: false,
51944         panels: [new CP("north", "North")]
51945     },
51946     west: {
51947         split:true,
51948         initialSize: 200,
51949         minSize: 175,
51950         maxSize: 400,
51951         titlebar: true,
51952         collapsible: true,
51953         panels: [new CP("west", {title: "West"})]
51954     },
51955     east: {
51956         split:true,
51957         initialSize: 202,
51958         minSize: 175,
51959         maxSize: 400,
51960         titlebar: true,
51961         collapsible: true,
51962         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51963     },
51964     south: {
51965         split:true,
51966         initialSize: 100,
51967         minSize: 100,
51968         maxSize: 200,
51969         titlebar: true,
51970         collapsible: true,
51971         panels: [new CP("south", {title: "South", closable: true})]
51972     },
51973     center: {
51974         titlebar: true,
51975         autoScroll:true,
51976         resizeTabs: true,
51977         minTabWidth: 50,
51978         preferredTabWidth: 150,
51979         panels: [
51980             new CP("center1", {title: "Close Me", closable: true}),
51981             new CP("center2", {title: "Center Panel", closable: false})
51982         ]
51983     }
51984 }, document.body);
51985
51986 layout.getRegion("center").showPanel("center1");
51987 </code></pre>
51988  * @param config
51989  * @param targetEl
51990  */
51991 Roo.BorderLayout.create = function(config, targetEl){
51992     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51993     layout.beginUpdate();
51994     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51995     for(var j = 0, jlen = regions.length; j < jlen; j++){
51996         var lr = regions[j];
51997         if(layout.regions[lr] && config[lr].panels){
51998             var r = layout.regions[lr];
51999             var ps = config[lr].panels;
52000             layout.addTypedPanels(r, ps);
52001         }
52002     }
52003     layout.endUpdate();
52004     return layout;
52005 };
52006
52007 // private
52008 Roo.BorderLayout.RegionFactory = {
52009     // private
52010     validRegions : ["north","south","east","west","center"],
52011
52012     // private
52013     create : function(target, mgr, config){
52014         target = target.toLowerCase();
52015         if(config.lightweight || config.basic){
52016             return new Roo.BasicLayoutRegion(mgr, config, target);
52017         }
52018         switch(target){
52019             case "north":
52020                 return new Roo.NorthLayoutRegion(mgr, config);
52021             case "south":
52022                 return new Roo.SouthLayoutRegion(mgr, config);
52023             case "east":
52024                 return new Roo.EastLayoutRegion(mgr, config);
52025             case "west":
52026                 return new Roo.WestLayoutRegion(mgr, config);
52027             case "center":
52028                 return new Roo.CenterLayoutRegion(mgr, config);
52029         }
52030         throw 'Layout region "'+target+'" not supported.';
52031     }
52032 };/*
52033  * Based on:
52034  * Ext JS Library 1.1.1
52035  * Copyright(c) 2006-2007, Ext JS, LLC.
52036  *
52037  * Originally Released Under LGPL - original licence link has changed is not relivant.
52038  *
52039  * Fork - LGPL
52040  * <script type="text/javascript">
52041  */
52042  
52043 /**
52044  * @class Roo.BasicLayoutRegion
52045  * @extends Roo.util.Observable
52046  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52047  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52048  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52049  */
52050 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52051     this.mgr = mgr;
52052     this.position  = pos;
52053     this.events = {
52054         /**
52055          * @scope Roo.BasicLayoutRegion
52056          */
52057         
52058         /**
52059          * @event beforeremove
52060          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52061          * @param {Roo.LayoutRegion} this
52062          * @param {Roo.ContentPanel} panel The panel
52063          * @param {Object} e The cancel event object
52064          */
52065         "beforeremove" : true,
52066         /**
52067          * @event invalidated
52068          * Fires when the layout for this region is changed.
52069          * @param {Roo.LayoutRegion} this
52070          */
52071         "invalidated" : true,
52072         /**
52073          * @event visibilitychange
52074          * Fires when this region is shown or hidden 
52075          * @param {Roo.LayoutRegion} this
52076          * @param {Boolean} visibility true or false
52077          */
52078         "visibilitychange" : true,
52079         /**
52080          * @event paneladded
52081          * Fires when a panel is added. 
52082          * @param {Roo.LayoutRegion} this
52083          * @param {Roo.ContentPanel} panel The panel
52084          */
52085         "paneladded" : true,
52086         /**
52087          * @event panelremoved
52088          * Fires when a panel is removed. 
52089          * @param {Roo.LayoutRegion} this
52090          * @param {Roo.ContentPanel} panel The panel
52091          */
52092         "panelremoved" : true,
52093         /**
52094          * @event beforecollapse
52095          * Fires when this region before collapse.
52096          * @param {Roo.LayoutRegion} this
52097          */
52098         "beforecollapse" : true,
52099         /**
52100          * @event collapsed
52101          * Fires when this region is collapsed.
52102          * @param {Roo.LayoutRegion} this
52103          */
52104         "collapsed" : true,
52105         /**
52106          * @event expanded
52107          * Fires when this region is expanded.
52108          * @param {Roo.LayoutRegion} this
52109          */
52110         "expanded" : true,
52111         /**
52112          * @event slideshow
52113          * Fires when this region is slid into view.
52114          * @param {Roo.LayoutRegion} this
52115          */
52116         "slideshow" : true,
52117         /**
52118          * @event slidehide
52119          * Fires when this region slides out of view. 
52120          * @param {Roo.LayoutRegion} this
52121          */
52122         "slidehide" : true,
52123         /**
52124          * @event panelactivated
52125          * Fires when a panel is activated. 
52126          * @param {Roo.LayoutRegion} this
52127          * @param {Roo.ContentPanel} panel The activated panel
52128          */
52129         "panelactivated" : true,
52130         /**
52131          * @event resized
52132          * Fires when the user resizes this region. 
52133          * @param {Roo.LayoutRegion} this
52134          * @param {Number} newSize The new size (width for east/west, height for north/south)
52135          */
52136         "resized" : true
52137     };
52138     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52139     this.panels = new Roo.util.MixedCollection();
52140     this.panels.getKey = this.getPanelId.createDelegate(this);
52141     this.box = null;
52142     this.activePanel = null;
52143     // ensure listeners are added...
52144     
52145     if (config.listeners || config.events) {
52146         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52147             listeners : config.listeners || {},
52148             events : config.events || {}
52149         });
52150     }
52151     
52152     if(skipConfig !== true){
52153         this.applyConfig(config);
52154     }
52155 };
52156
52157 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52158     getPanelId : function(p){
52159         return p.getId();
52160     },
52161     
52162     applyConfig : function(config){
52163         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52164         this.config = config;
52165         
52166     },
52167     
52168     /**
52169      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52170      * the width, for horizontal (north, south) the height.
52171      * @param {Number} newSize The new width or height
52172      */
52173     resizeTo : function(newSize){
52174         var el = this.el ? this.el :
52175                  (this.activePanel ? this.activePanel.getEl() : null);
52176         if(el){
52177             switch(this.position){
52178                 case "east":
52179                 case "west":
52180                     el.setWidth(newSize);
52181                     this.fireEvent("resized", this, newSize);
52182                 break;
52183                 case "north":
52184                 case "south":
52185                     el.setHeight(newSize);
52186                     this.fireEvent("resized", this, newSize);
52187                 break;                
52188             }
52189         }
52190     },
52191     
52192     getBox : function(){
52193         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52194     },
52195     
52196     getMargins : function(){
52197         return this.margins;
52198     },
52199     
52200     updateBox : function(box){
52201         this.box = box;
52202         var el = this.activePanel.getEl();
52203         el.dom.style.left = box.x + "px";
52204         el.dom.style.top = box.y + "px";
52205         this.activePanel.setSize(box.width, box.height);
52206     },
52207     
52208     /**
52209      * Returns the container element for this region.
52210      * @return {Roo.Element}
52211      */
52212     getEl : function(){
52213         return this.activePanel;
52214     },
52215     
52216     /**
52217      * Returns true if this region is currently visible.
52218      * @return {Boolean}
52219      */
52220     isVisible : function(){
52221         return this.activePanel ? true : false;
52222     },
52223     
52224     setActivePanel : function(panel){
52225         panel = this.getPanel(panel);
52226         if(this.activePanel && this.activePanel != panel){
52227             this.activePanel.setActiveState(false);
52228             this.activePanel.getEl().setLeftTop(-10000,-10000);
52229         }
52230         this.activePanel = panel;
52231         panel.setActiveState(true);
52232         if(this.box){
52233             panel.setSize(this.box.width, this.box.height);
52234         }
52235         this.fireEvent("panelactivated", this, panel);
52236         this.fireEvent("invalidated");
52237     },
52238     
52239     /**
52240      * Show the specified panel.
52241      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52242      * @return {Roo.ContentPanel} The shown panel or null
52243      */
52244     showPanel : function(panel){
52245         if(panel = this.getPanel(panel)){
52246             this.setActivePanel(panel);
52247         }
52248         return panel;
52249     },
52250     
52251     /**
52252      * Get the active panel for this region.
52253      * @return {Roo.ContentPanel} The active panel or null
52254      */
52255     getActivePanel : function(){
52256         return this.activePanel;
52257     },
52258     
52259     /**
52260      * Add the passed ContentPanel(s)
52261      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52262      * @return {Roo.ContentPanel} The panel added (if only one was added)
52263      */
52264     add : function(panel){
52265         if(arguments.length > 1){
52266             for(var i = 0, len = arguments.length; i < len; i++) {
52267                 this.add(arguments[i]);
52268             }
52269             return null;
52270         }
52271         if(this.hasPanel(panel)){
52272             this.showPanel(panel);
52273             return panel;
52274         }
52275         var el = panel.getEl();
52276         if(el.dom.parentNode != this.mgr.el.dom){
52277             this.mgr.el.dom.appendChild(el.dom);
52278         }
52279         if(panel.setRegion){
52280             panel.setRegion(this);
52281         }
52282         this.panels.add(panel);
52283         el.setStyle("position", "absolute");
52284         if(!panel.background){
52285             this.setActivePanel(panel);
52286             if(this.config.initialSize && this.panels.getCount()==1){
52287                 this.resizeTo(this.config.initialSize);
52288             }
52289         }
52290         this.fireEvent("paneladded", this, panel);
52291         return panel;
52292     },
52293     
52294     /**
52295      * Returns true if the panel is in this region.
52296      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52297      * @return {Boolean}
52298      */
52299     hasPanel : function(panel){
52300         if(typeof panel == "object"){ // must be panel obj
52301             panel = panel.getId();
52302         }
52303         return this.getPanel(panel) ? true : false;
52304     },
52305     
52306     /**
52307      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52308      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52309      * @param {Boolean} preservePanel Overrides the config preservePanel option
52310      * @return {Roo.ContentPanel} The panel that was removed
52311      */
52312     remove : function(panel, preservePanel){
52313         panel = this.getPanel(panel);
52314         if(!panel){
52315             return null;
52316         }
52317         var e = {};
52318         this.fireEvent("beforeremove", this, panel, e);
52319         if(e.cancel === true){
52320             return null;
52321         }
52322         var panelId = panel.getId();
52323         this.panels.removeKey(panelId);
52324         return panel;
52325     },
52326     
52327     /**
52328      * Returns the panel specified or null if it's not in this region.
52329      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52330      * @return {Roo.ContentPanel}
52331      */
52332     getPanel : function(id){
52333         if(typeof id == "object"){ // must be panel obj
52334             return id;
52335         }
52336         return this.panels.get(id);
52337     },
52338     
52339     /**
52340      * Returns this regions position (north/south/east/west/center).
52341      * @return {String} 
52342      */
52343     getPosition: function(){
52344         return this.position;    
52345     }
52346 });/*
52347  * Based on:
52348  * Ext JS Library 1.1.1
52349  * Copyright(c) 2006-2007, Ext JS, LLC.
52350  *
52351  * Originally Released Under LGPL - original licence link has changed is not relivant.
52352  *
52353  * Fork - LGPL
52354  * <script type="text/javascript">
52355  */
52356  
52357 /**
52358  * @class Roo.LayoutRegion
52359  * @extends Roo.BasicLayoutRegion
52360  * This class represents a region in a layout manager.
52361  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52362  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52363  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52364  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52365  * @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})
52366  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52367  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52368  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52369  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52370  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52371  * @cfg {String}    title           The title for the region (overrides panel titles)
52372  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52373  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52374  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52375  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52376  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52377  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52378  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52379  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52380  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52381  * @cfg {Boolean}   showPin         True to show a pin button
52382  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52383  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52384  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52385  * @cfg {Number}    width           For East/West panels
52386  * @cfg {Number}    height          For North/South panels
52387  * @cfg {Boolean}   split           To show the splitter
52388  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52389  */
52390 Roo.LayoutRegion = function(mgr, config, pos){
52391     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52392     var dh = Roo.DomHelper;
52393     /** This region's container element 
52394     * @type Roo.Element */
52395     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52396     /** This region's title element 
52397     * @type Roo.Element */
52398
52399     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52400         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52401         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52402     ]}, true);
52403     this.titleEl.enableDisplayMode();
52404     /** This region's title text element 
52405     * @type HTMLElement */
52406     this.titleTextEl = this.titleEl.dom.firstChild;
52407     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52408     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52409     this.closeBtn.enableDisplayMode();
52410     this.closeBtn.on("click", this.closeClicked, this);
52411     this.closeBtn.hide();
52412
52413     this.createBody(config);
52414     this.visible = true;
52415     this.collapsed = false;
52416
52417     if(config.hideWhenEmpty){
52418         this.hide();
52419         this.on("paneladded", this.validateVisibility, this);
52420         this.on("panelremoved", this.validateVisibility, this);
52421     }
52422     this.applyConfig(config);
52423 };
52424
52425 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52426
52427     createBody : function(){
52428         /** This region's body element 
52429         * @type Roo.Element */
52430         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52431     },
52432
52433     applyConfig : function(c){
52434         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52435             var dh = Roo.DomHelper;
52436             if(c.titlebar !== false){
52437                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52438                 this.collapseBtn.on("click", this.collapse, this);
52439                 this.collapseBtn.enableDisplayMode();
52440
52441                 if(c.showPin === true || this.showPin){
52442                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52443                     this.stickBtn.enableDisplayMode();
52444                     this.stickBtn.on("click", this.expand, this);
52445                     this.stickBtn.hide();
52446                 }
52447             }
52448             /** This region's collapsed element
52449             * @type Roo.Element */
52450             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52451                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52452             ]}, true);
52453             if(c.floatable !== false){
52454                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52455                this.collapsedEl.on("click", this.collapseClick, this);
52456             }
52457
52458             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52459                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52460                    id: "message", unselectable: "on", style:{"float":"left"}});
52461                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52462              }
52463             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52464             this.expandBtn.on("click", this.expand, this);
52465         }
52466         if(this.collapseBtn){
52467             this.collapseBtn.setVisible(c.collapsible == true);
52468         }
52469         this.cmargins = c.cmargins || this.cmargins ||
52470                          (this.position == "west" || this.position == "east" ?
52471                              {top: 0, left: 2, right:2, bottom: 0} :
52472                              {top: 2, left: 0, right:0, bottom: 2});
52473         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52474         this.bottomTabs = c.tabPosition != "top";
52475         this.autoScroll = c.autoScroll || false;
52476         if(this.autoScroll){
52477             this.bodyEl.setStyle("overflow", "auto");
52478         }else{
52479             this.bodyEl.setStyle("overflow", "hidden");
52480         }
52481         //if(c.titlebar !== false){
52482             if((!c.titlebar && !c.title) || c.titlebar === false){
52483                 this.titleEl.hide();
52484             }else{
52485                 this.titleEl.show();
52486                 if(c.title){
52487                     this.titleTextEl.innerHTML = c.title;
52488                 }
52489             }
52490         //}
52491         this.duration = c.duration || .30;
52492         this.slideDuration = c.slideDuration || .45;
52493         this.config = c;
52494         if(c.collapsed){
52495             this.collapse(true);
52496         }
52497         if(c.hidden){
52498             this.hide();
52499         }
52500     },
52501     /**
52502      * Returns true if this region is currently visible.
52503      * @return {Boolean}
52504      */
52505     isVisible : function(){
52506         return this.visible;
52507     },
52508
52509     /**
52510      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52511      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52512      */
52513     setCollapsedTitle : function(title){
52514         title = title || "&#160;";
52515         if(this.collapsedTitleTextEl){
52516             this.collapsedTitleTextEl.innerHTML = title;
52517         }
52518     },
52519
52520     getBox : function(){
52521         var b;
52522         if(!this.collapsed){
52523             b = this.el.getBox(false, true);
52524         }else{
52525             b = this.collapsedEl.getBox(false, true);
52526         }
52527         return b;
52528     },
52529
52530     getMargins : function(){
52531         return this.collapsed ? this.cmargins : this.margins;
52532     },
52533
52534     highlight : function(){
52535         this.el.addClass("x-layout-panel-dragover");
52536     },
52537
52538     unhighlight : function(){
52539         this.el.removeClass("x-layout-panel-dragover");
52540     },
52541
52542     updateBox : function(box){
52543         this.box = box;
52544         if(!this.collapsed){
52545             this.el.dom.style.left = box.x + "px";
52546             this.el.dom.style.top = box.y + "px";
52547             this.updateBody(box.width, box.height);
52548         }else{
52549             this.collapsedEl.dom.style.left = box.x + "px";
52550             this.collapsedEl.dom.style.top = box.y + "px";
52551             this.collapsedEl.setSize(box.width, box.height);
52552         }
52553         if(this.tabs){
52554             this.tabs.autoSizeTabs();
52555         }
52556     },
52557
52558     updateBody : function(w, h){
52559         if(w !== null){
52560             this.el.setWidth(w);
52561             w -= this.el.getBorderWidth("rl");
52562             if(this.config.adjustments){
52563                 w += this.config.adjustments[0];
52564             }
52565         }
52566         if(h !== null){
52567             this.el.setHeight(h);
52568             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52569             h -= this.el.getBorderWidth("tb");
52570             if(this.config.adjustments){
52571                 h += this.config.adjustments[1];
52572             }
52573             this.bodyEl.setHeight(h);
52574             if(this.tabs){
52575                 h = this.tabs.syncHeight(h);
52576             }
52577         }
52578         if(this.panelSize){
52579             w = w !== null ? w : this.panelSize.width;
52580             h = h !== null ? h : this.panelSize.height;
52581         }
52582         if(this.activePanel){
52583             var el = this.activePanel.getEl();
52584             w = w !== null ? w : el.getWidth();
52585             h = h !== null ? h : el.getHeight();
52586             this.panelSize = {width: w, height: h};
52587             this.activePanel.setSize(w, h);
52588         }
52589         if(Roo.isIE && this.tabs){
52590             this.tabs.el.repaint();
52591         }
52592     },
52593
52594     /**
52595      * Returns the container element for this region.
52596      * @return {Roo.Element}
52597      */
52598     getEl : function(){
52599         return this.el;
52600     },
52601
52602     /**
52603      * Hides this region.
52604      */
52605     hide : function(){
52606         if(!this.collapsed){
52607             this.el.dom.style.left = "-2000px";
52608             this.el.hide();
52609         }else{
52610             this.collapsedEl.dom.style.left = "-2000px";
52611             this.collapsedEl.hide();
52612         }
52613         this.visible = false;
52614         this.fireEvent("visibilitychange", this, false);
52615     },
52616
52617     /**
52618      * Shows this region if it was previously hidden.
52619      */
52620     show : function(){
52621         if(!this.collapsed){
52622             this.el.show();
52623         }else{
52624             this.collapsedEl.show();
52625         }
52626         this.visible = true;
52627         this.fireEvent("visibilitychange", this, true);
52628     },
52629
52630     closeClicked : function(){
52631         if(this.activePanel){
52632             this.remove(this.activePanel);
52633         }
52634     },
52635
52636     collapseClick : function(e){
52637         if(this.isSlid){
52638            e.stopPropagation();
52639            this.slideIn();
52640         }else{
52641            e.stopPropagation();
52642            this.slideOut();
52643         }
52644     },
52645
52646     /**
52647      * Collapses this region.
52648      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52649      */
52650     collapse : function(skipAnim, skipCheck){
52651         if(this.collapsed) {
52652             return;
52653         }
52654         
52655         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52656             
52657             this.collapsed = true;
52658             if(this.split){
52659                 this.split.el.hide();
52660             }
52661             if(this.config.animate && skipAnim !== true){
52662                 this.fireEvent("invalidated", this);
52663                 this.animateCollapse();
52664             }else{
52665                 this.el.setLocation(-20000,-20000);
52666                 this.el.hide();
52667                 this.collapsedEl.show();
52668                 this.fireEvent("collapsed", this);
52669                 this.fireEvent("invalidated", this);
52670             }
52671         }
52672         
52673     },
52674
52675     animateCollapse : function(){
52676         // overridden
52677     },
52678
52679     /**
52680      * Expands this region if it was previously collapsed.
52681      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52682      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52683      */
52684     expand : function(e, skipAnim){
52685         if(e) {
52686             e.stopPropagation();
52687         }
52688         if(!this.collapsed || this.el.hasActiveFx()) {
52689             return;
52690         }
52691         if(this.isSlid){
52692             this.afterSlideIn();
52693             skipAnim = true;
52694         }
52695         this.collapsed = false;
52696         if(this.config.animate && skipAnim !== true){
52697             this.animateExpand();
52698         }else{
52699             this.el.show();
52700             if(this.split){
52701                 this.split.el.show();
52702             }
52703             this.collapsedEl.setLocation(-2000,-2000);
52704             this.collapsedEl.hide();
52705             this.fireEvent("invalidated", this);
52706             this.fireEvent("expanded", this);
52707         }
52708     },
52709
52710     animateExpand : function(){
52711         // overridden
52712     },
52713
52714     initTabs : function()
52715     {
52716         this.bodyEl.setStyle("overflow", "hidden");
52717         var ts = new Roo.TabPanel(
52718                 this.bodyEl.dom,
52719                 {
52720                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52721                     disableTooltips: this.config.disableTabTips,
52722                     toolbar : this.config.toolbar
52723                 }
52724         );
52725         if(this.config.hideTabs){
52726             ts.stripWrap.setDisplayed(false);
52727         }
52728         this.tabs = ts;
52729         ts.resizeTabs = this.config.resizeTabs === true;
52730         ts.minTabWidth = this.config.minTabWidth || 40;
52731         ts.maxTabWidth = this.config.maxTabWidth || 250;
52732         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52733         ts.monitorResize = false;
52734         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52735         ts.bodyEl.addClass('x-layout-tabs-body');
52736         this.panels.each(this.initPanelAsTab, this);
52737     },
52738
52739     initPanelAsTab : function(panel){
52740         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52741                     this.config.closeOnTab && panel.isClosable());
52742         if(panel.tabTip !== undefined){
52743             ti.setTooltip(panel.tabTip);
52744         }
52745         ti.on("activate", function(){
52746               this.setActivePanel(panel);
52747         }, this);
52748         if(this.config.closeOnTab){
52749             ti.on("beforeclose", function(t, e){
52750                 e.cancel = true;
52751                 this.remove(panel);
52752             }, this);
52753         }
52754         return ti;
52755     },
52756
52757     updatePanelTitle : function(panel, title){
52758         if(this.activePanel == panel){
52759             this.updateTitle(title);
52760         }
52761         if(this.tabs){
52762             var ti = this.tabs.getTab(panel.getEl().id);
52763             ti.setText(title);
52764             if(panel.tabTip !== undefined){
52765                 ti.setTooltip(panel.tabTip);
52766             }
52767         }
52768     },
52769
52770     updateTitle : function(title){
52771         if(this.titleTextEl && !this.config.title){
52772             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52773         }
52774     },
52775
52776     setActivePanel : function(panel){
52777         panel = this.getPanel(panel);
52778         if(this.activePanel && this.activePanel != panel){
52779             this.activePanel.setActiveState(false);
52780         }
52781         this.activePanel = panel;
52782         panel.setActiveState(true);
52783         if(this.panelSize){
52784             panel.setSize(this.panelSize.width, this.panelSize.height);
52785         }
52786         if(this.closeBtn){
52787             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52788         }
52789         this.updateTitle(panel.getTitle());
52790         if(this.tabs){
52791             this.fireEvent("invalidated", this);
52792         }
52793         this.fireEvent("panelactivated", this, panel);
52794     },
52795
52796     /**
52797      * Shows the specified panel.
52798      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52799      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52800      */
52801     showPanel : function(panel)
52802     {
52803         panel = this.getPanel(panel);
52804         if(panel){
52805             if(this.tabs){
52806                 var tab = this.tabs.getTab(panel.getEl().id);
52807                 if(tab.isHidden()){
52808                     this.tabs.unhideTab(tab.id);
52809                 }
52810                 tab.activate();
52811             }else{
52812                 this.setActivePanel(panel);
52813             }
52814         }
52815         return panel;
52816     },
52817
52818     /**
52819      * Get the active panel for this region.
52820      * @return {Roo.ContentPanel} The active panel or null
52821      */
52822     getActivePanel : function(){
52823         return this.activePanel;
52824     },
52825
52826     validateVisibility : function(){
52827         if(this.panels.getCount() < 1){
52828             this.updateTitle("&#160;");
52829             this.closeBtn.hide();
52830             this.hide();
52831         }else{
52832             if(!this.isVisible()){
52833                 this.show();
52834             }
52835         }
52836     },
52837
52838     /**
52839      * Adds the passed ContentPanel(s) to this region.
52840      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52841      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52842      */
52843     add : function(panel){
52844         if(arguments.length > 1){
52845             for(var i = 0, len = arguments.length; i < len; i++) {
52846                 this.add(arguments[i]);
52847             }
52848             return null;
52849         }
52850         if(this.hasPanel(panel)){
52851             this.showPanel(panel);
52852             return panel;
52853         }
52854         panel.setRegion(this);
52855         this.panels.add(panel);
52856         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52857             this.bodyEl.dom.appendChild(panel.getEl().dom);
52858             if(panel.background !== true){
52859                 this.setActivePanel(panel);
52860             }
52861             this.fireEvent("paneladded", this, panel);
52862             return panel;
52863         }
52864         if(!this.tabs){
52865             this.initTabs();
52866         }else{
52867             this.initPanelAsTab(panel);
52868         }
52869         if(panel.background !== true){
52870             this.tabs.activate(panel.getEl().id);
52871         }
52872         this.fireEvent("paneladded", this, panel);
52873         return panel;
52874     },
52875
52876     /**
52877      * Hides the tab for the specified panel.
52878      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52879      */
52880     hidePanel : function(panel){
52881         if(this.tabs && (panel = this.getPanel(panel))){
52882             this.tabs.hideTab(panel.getEl().id);
52883         }
52884     },
52885
52886     /**
52887      * Unhides the tab for a previously hidden panel.
52888      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52889      */
52890     unhidePanel : function(panel){
52891         if(this.tabs && (panel = this.getPanel(panel))){
52892             this.tabs.unhideTab(panel.getEl().id);
52893         }
52894     },
52895
52896     clearPanels : function(){
52897         while(this.panels.getCount() > 0){
52898              this.remove(this.panels.first());
52899         }
52900     },
52901
52902     /**
52903      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52904      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52905      * @param {Boolean} preservePanel Overrides the config preservePanel option
52906      * @return {Roo.ContentPanel} The panel that was removed
52907      */
52908     remove : function(panel, preservePanel){
52909         panel = this.getPanel(panel);
52910         if(!panel){
52911             return null;
52912         }
52913         var e = {};
52914         this.fireEvent("beforeremove", this, panel, e);
52915         if(e.cancel === true){
52916             return null;
52917         }
52918         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52919         var panelId = panel.getId();
52920         this.panels.removeKey(panelId);
52921         if(preservePanel){
52922             document.body.appendChild(panel.getEl().dom);
52923         }
52924         if(this.tabs){
52925             this.tabs.removeTab(panel.getEl().id);
52926         }else if (!preservePanel){
52927             this.bodyEl.dom.removeChild(panel.getEl().dom);
52928         }
52929         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52930             var p = this.panels.first();
52931             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52932             tempEl.appendChild(p.getEl().dom);
52933             this.bodyEl.update("");
52934             this.bodyEl.dom.appendChild(p.getEl().dom);
52935             tempEl = null;
52936             this.updateTitle(p.getTitle());
52937             this.tabs = null;
52938             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52939             this.setActivePanel(p);
52940         }
52941         panel.setRegion(null);
52942         if(this.activePanel == panel){
52943             this.activePanel = null;
52944         }
52945         if(this.config.autoDestroy !== false && preservePanel !== true){
52946             try{panel.destroy();}catch(e){}
52947         }
52948         this.fireEvent("panelremoved", this, panel);
52949         return panel;
52950     },
52951
52952     /**
52953      * Returns the TabPanel component used by this region
52954      * @return {Roo.TabPanel}
52955      */
52956     getTabs : function(){
52957         return this.tabs;
52958     },
52959
52960     createTool : function(parentEl, className){
52961         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52962             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52963         btn.addClassOnOver("x-layout-tools-button-over");
52964         return btn;
52965     }
52966 });/*
52967  * Based on:
52968  * Ext JS Library 1.1.1
52969  * Copyright(c) 2006-2007, Ext JS, LLC.
52970  *
52971  * Originally Released Under LGPL - original licence link has changed is not relivant.
52972  *
52973  * Fork - LGPL
52974  * <script type="text/javascript">
52975  */
52976  
52977
52978
52979 /**
52980  * @class Roo.SplitLayoutRegion
52981  * @extends Roo.LayoutRegion
52982  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52983  */
52984 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52985     this.cursor = cursor;
52986     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52987 };
52988
52989 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52990     splitTip : "Drag to resize.",
52991     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52992     useSplitTips : false,
52993
52994     applyConfig : function(config){
52995         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52996         if(config.split){
52997             if(!this.split){
52998                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52999                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53000                 /** The SplitBar for this region 
53001                 * @type Roo.SplitBar */
53002                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53003                 this.split.on("moved", this.onSplitMove, this);
53004                 this.split.useShim = config.useShim === true;
53005                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53006                 if(this.useSplitTips){
53007                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53008                 }
53009                 if(config.collapsible){
53010                     this.split.el.on("dblclick", this.collapse,  this);
53011                 }
53012             }
53013             if(typeof config.minSize != "undefined"){
53014                 this.split.minSize = config.minSize;
53015             }
53016             if(typeof config.maxSize != "undefined"){
53017                 this.split.maxSize = config.maxSize;
53018             }
53019             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53020                 this.hideSplitter();
53021             }
53022         }
53023     },
53024
53025     getHMaxSize : function(){
53026          var cmax = this.config.maxSize || 10000;
53027          var center = this.mgr.getRegion("center");
53028          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53029     },
53030
53031     getVMaxSize : function(){
53032          var cmax = this.config.maxSize || 10000;
53033          var center = this.mgr.getRegion("center");
53034          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53035     },
53036
53037     onSplitMove : function(split, newSize){
53038         this.fireEvent("resized", this, newSize);
53039     },
53040     
53041     /** 
53042      * Returns the {@link Roo.SplitBar} for this region.
53043      * @return {Roo.SplitBar}
53044      */
53045     getSplitBar : function(){
53046         return this.split;
53047     },
53048     
53049     hide : function(){
53050         this.hideSplitter();
53051         Roo.SplitLayoutRegion.superclass.hide.call(this);
53052     },
53053
53054     hideSplitter : function(){
53055         if(this.split){
53056             this.split.el.setLocation(-2000,-2000);
53057             this.split.el.hide();
53058         }
53059     },
53060
53061     show : function(){
53062         if(this.split){
53063             this.split.el.show();
53064         }
53065         Roo.SplitLayoutRegion.superclass.show.call(this);
53066     },
53067     
53068     beforeSlide: function(){
53069         if(Roo.isGecko){// firefox overflow auto bug workaround
53070             this.bodyEl.clip();
53071             if(this.tabs) {
53072                 this.tabs.bodyEl.clip();
53073             }
53074             if(this.activePanel){
53075                 this.activePanel.getEl().clip();
53076                 
53077                 if(this.activePanel.beforeSlide){
53078                     this.activePanel.beforeSlide();
53079                 }
53080             }
53081         }
53082     },
53083     
53084     afterSlide : function(){
53085         if(Roo.isGecko){// firefox overflow auto bug workaround
53086             this.bodyEl.unclip();
53087             if(this.tabs) {
53088                 this.tabs.bodyEl.unclip();
53089             }
53090             if(this.activePanel){
53091                 this.activePanel.getEl().unclip();
53092                 if(this.activePanel.afterSlide){
53093                     this.activePanel.afterSlide();
53094                 }
53095             }
53096         }
53097     },
53098
53099     initAutoHide : function(){
53100         if(this.autoHide !== false){
53101             if(!this.autoHideHd){
53102                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53103                 this.autoHideHd = {
53104                     "mouseout": function(e){
53105                         if(!e.within(this.el, true)){
53106                             st.delay(500);
53107                         }
53108                     },
53109                     "mouseover" : function(e){
53110                         st.cancel();
53111                     },
53112                     scope : this
53113                 };
53114             }
53115             this.el.on(this.autoHideHd);
53116         }
53117     },
53118
53119     clearAutoHide : function(){
53120         if(this.autoHide !== false){
53121             this.el.un("mouseout", this.autoHideHd.mouseout);
53122             this.el.un("mouseover", this.autoHideHd.mouseover);
53123         }
53124     },
53125
53126     clearMonitor : function(){
53127         Roo.get(document).un("click", this.slideInIf, this);
53128     },
53129
53130     // these names are backwards but not changed for compat
53131     slideOut : function(){
53132         if(this.isSlid || this.el.hasActiveFx()){
53133             return;
53134         }
53135         this.isSlid = true;
53136         if(this.collapseBtn){
53137             this.collapseBtn.hide();
53138         }
53139         this.closeBtnState = this.closeBtn.getStyle('display');
53140         this.closeBtn.hide();
53141         if(this.stickBtn){
53142             this.stickBtn.show();
53143         }
53144         this.el.show();
53145         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53146         this.beforeSlide();
53147         this.el.setStyle("z-index", 10001);
53148         this.el.slideIn(this.getSlideAnchor(), {
53149             callback: function(){
53150                 this.afterSlide();
53151                 this.initAutoHide();
53152                 Roo.get(document).on("click", this.slideInIf, this);
53153                 this.fireEvent("slideshow", this);
53154             },
53155             scope: this,
53156             block: true
53157         });
53158     },
53159
53160     afterSlideIn : function(){
53161         this.clearAutoHide();
53162         this.isSlid = false;
53163         this.clearMonitor();
53164         this.el.setStyle("z-index", "");
53165         if(this.collapseBtn){
53166             this.collapseBtn.show();
53167         }
53168         this.closeBtn.setStyle('display', this.closeBtnState);
53169         if(this.stickBtn){
53170             this.stickBtn.hide();
53171         }
53172         this.fireEvent("slidehide", this);
53173     },
53174
53175     slideIn : function(cb){
53176         if(!this.isSlid || this.el.hasActiveFx()){
53177             Roo.callback(cb);
53178             return;
53179         }
53180         this.isSlid = false;
53181         this.beforeSlide();
53182         this.el.slideOut(this.getSlideAnchor(), {
53183             callback: function(){
53184                 this.el.setLeftTop(-10000, -10000);
53185                 this.afterSlide();
53186                 this.afterSlideIn();
53187                 Roo.callback(cb);
53188             },
53189             scope: this,
53190             block: true
53191         });
53192     },
53193     
53194     slideInIf : function(e){
53195         if(!e.within(this.el)){
53196             this.slideIn();
53197         }
53198     },
53199
53200     animateCollapse : function(){
53201         this.beforeSlide();
53202         this.el.setStyle("z-index", 20000);
53203         var anchor = this.getSlideAnchor();
53204         this.el.slideOut(anchor, {
53205             callback : function(){
53206                 this.el.setStyle("z-index", "");
53207                 this.collapsedEl.slideIn(anchor, {duration:.3});
53208                 this.afterSlide();
53209                 this.el.setLocation(-10000,-10000);
53210                 this.el.hide();
53211                 this.fireEvent("collapsed", this);
53212             },
53213             scope: this,
53214             block: true
53215         });
53216     },
53217
53218     animateExpand : function(){
53219         this.beforeSlide();
53220         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53221         this.el.setStyle("z-index", 20000);
53222         this.collapsedEl.hide({
53223             duration:.1
53224         });
53225         this.el.slideIn(this.getSlideAnchor(), {
53226             callback : function(){
53227                 this.el.setStyle("z-index", "");
53228                 this.afterSlide();
53229                 if(this.split){
53230                     this.split.el.show();
53231                 }
53232                 this.fireEvent("invalidated", this);
53233                 this.fireEvent("expanded", this);
53234             },
53235             scope: this,
53236             block: true
53237         });
53238     },
53239
53240     anchors : {
53241         "west" : "left",
53242         "east" : "right",
53243         "north" : "top",
53244         "south" : "bottom"
53245     },
53246
53247     sanchors : {
53248         "west" : "l",
53249         "east" : "r",
53250         "north" : "t",
53251         "south" : "b"
53252     },
53253
53254     canchors : {
53255         "west" : "tl-tr",
53256         "east" : "tr-tl",
53257         "north" : "tl-bl",
53258         "south" : "bl-tl"
53259     },
53260
53261     getAnchor : function(){
53262         return this.anchors[this.position];
53263     },
53264
53265     getCollapseAnchor : function(){
53266         return this.canchors[this.position];
53267     },
53268
53269     getSlideAnchor : function(){
53270         return this.sanchors[this.position];
53271     },
53272
53273     getAlignAdj : function(){
53274         var cm = this.cmargins;
53275         switch(this.position){
53276             case "west":
53277                 return [0, 0];
53278             break;
53279             case "east":
53280                 return [0, 0];
53281             break;
53282             case "north":
53283                 return [0, 0];
53284             break;
53285             case "south":
53286                 return [0, 0];
53287             break;
53288         }
53289     },
53290
53291     getExpandAdj : function(){
53292         var c = this.collapsedEl, cm = this.cmargins;
53293         switch(this.position){
53294             case "west":
53295                 return [-(cm.right+c.getWidth()+cm.left), 0];
53296             break;
53297             case "east":
53298                 return [cm.right+c.getWidth()+cm.left, 0];
53299             break;
53300             case "north":
53301                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53302             break;
53303             case "south":
53304                 return [0, cm.top+cm.bottom+c.getHeight()];
53305             break;
53306         }
53307     }
53308 });/*
53309  * Based on:
53310  * Ext JS Library 1.1.1
53311  * Copyright(c) 2006-2007, Ext JS, LLC.
53312  *
53313  * Originally Released Under LGPL - original licence link has changed is not relivant.
53314  *
53315  * Fork - LGPL
53316  * <script type="text/javascript">
53317  */
53318 /*
53319  * These classes are private internal classes
53320  */
53321 Roo.CenterLayoutRegion = function(mgr, config){
53322     Roo.LayoutRegion.call(this, mgr, config, "center");
53323     this.visible = true;
53324     this.minWidth = config.minWidth || 20;
53325     this.minHeight = config.minHeight || 20;
53326 };
53327
53328 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53329     hide : function(){
53330         // center panel can't be hidden
53331     },
53332     
53333     show : function(){
53334         // center panel can't be hidden
53335     },
53336     
53337     getMinWidth: function(){
53338         return this.minWidth;
53339     },
53340     
53341     getMinHeight: function(){
53342         return this.minHeight;
53343     }
53344 });
53345
53346
53347 Roo.NorthLayoutRegion = function(mgr, config){
53348     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53349     if(this.split){
53350         this.split.placement = Roo.SplitBar.TOP;
53351         this.split.orientation = Roo.SplitBar.VERTICAL;
53352         this.split.el.addClass("x-layout-split-v");
53353     }
53354     var size = config.initialSize || config.height;
53355     if(typeof size != "undefined"){
53356         this.el.setHeight(size);
53357     }
53358 };
53359 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53360     orientation: Roo.SplitBar.VERTICAL,
53361     getBox : function(){
53362         if(this.collapsed){
53363             return this.collapsedEl.getBox();
53364         }
53365         var box = this.el.getBox();
53366         if(this.split){
53367             box.height += this.split.el.getHeight();
53368         }
53369         return box;
53370     },
53371     
53372     updateBox : function(box){
53373         if(this.split && !this.collapsed){
53374             box.height -= this.split.el.getHeight();
53375             this.split.el.setLeft(box.x);
53376             this.split.el.setTop(box.y+box.height);
53377             this.split.el.setWidth(box.width);
53378         }
53379         if(this.collapsed){
53380             this.updateBody(box.width, null);
53381         }
53382         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53383     }
53384 });
53385
53386 Roo.SouthLayoutRegion = function(mgr, config){
53387     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53388     if(this.split){
53389         this.split.placement = Roo.SplitBar.BOTTOM;
53390         this.split.orientation = Roo.SplitBar.VERTICAL;
53391         this.split.el.addClass("x-layout-split-v");
53392     }
53393     var size = config.initialSize || config.height;
53394     if(typeof size != "undefined"){
53395         this.el.setHeight(size);
53396     }
53397 };
53398 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53399     orientation: Roo.SplitBar.VERTICAL,
53400     getBox : function(){
53401         if(this.collapsed){
53402             return this.collapsedEl.getBox();
53403         }
53404         var box = this.el.getBox();
53405         if(this.split){
53406             var sh = this.split.el.getHeight();
53407             box.height += sh;
53408             box.y -= sh;
53409         }
53410         return box;
53411     },
53412     
53413     updateBox : function(box){
53414         if(this.split && !this.collapsed){
53415             var sh = this.split.el.getHeight();
53416             box.height -= sh;
53417             box.y += sh;
53418             this.split.el.setLeft(box.x);
53419             this.split.el.setTop(box.y-sh);
53420             this.split.el.setWidth(box.width);
53421         }
53422         if(this.collapsed){
53423             this.updateBody(box.width, null);
53424         }
53425         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53426     }
53427 });
53428
53429 Roo.EastLayoutRegion = function(mgr, config){
53430     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53431     if(this.split){
53432         this.split.placement = Roo.SplitBar.RIGHT;
53433         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53434         this.split.el.addClass("x-layout-split-h");
53435     }
53436     var size = config.initialSize || config.width;
53437     if(typeof size != "undefined"){
53438         this.el.setWidth(size);
53439     }
53440 };
53441 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53442     orientation: Roo.SplitBar.HORIZONTAL,
53443     getBox : function(){
53444         if(this.collapsed){
53445             return this.collapsedEl.getBox();
53446         }
53447         var box = this.el.getBox();
53448         if(this.split){
53449             var sw = this.split.el.getWidth();
53450             box.width += sw;
53451             box.x -= sw;
53452         }
53453         return box;
53454     },
53455
53456     updateBox : function(box){
53457         if(this.split && !this.collapsed){
53458             var sw = this.split.el.getWidth();
53459             box.width -= sw;
53460             this.split.el.setLeft(box.x);
53461             this.split.el.setTop(box.y);
53462             this.split.el.setHeight(box.height);
53463             box.x += sw;
53464         }
53465         if(this.collapsed){
53466             this.updateBody(null, box.height);
53467         }
53468         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53469     }
53470 });
53471
53472 Roo.WestLayoutRegion = function(mgr, config){
53473     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53474     if(this.split){
53475         this.split.placement = Roo.SplitBar.LEFT;
53476         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53477         this.split.el.addClass("x-layout-split-h");
53478     }
53479     var size = config.initialSize || config.width;
53480     if(typeof size != "undefined"){
53481         this.el.setWidth(size);
53482     }
53483 };
53484 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53485     orientation: Roo.SplitBar.HORIZONTAL,
53486     getBox : function(){
53487         if(this.collapsed){
53488             return this.collapsedEl.getBox();
53489         }
53490         var box = this.el.getBox();
53491         if(this.split){
53492             box.width += this.split.el.getWidth();
53493         }
53494         return box;
53495     },
53496     
53497     updateBox : function(box){
53498         if(this.split && !this.collapsed){
53499             var sw = this.split.el.getWidth();
53500             box.width -= sw;
53501             this.split.el.setLeft(box.x+box.width);
53502             this.split.el.setTop(box.y);
53503             this.split.el.setHeight(box.height);
53504         }
53505         if(this.collapsed){
53506             this.updateBody(null, box.height);
53507         }
53508         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53509     }
53510 });
53511 /*
53512  * Based on:
53513  * Ext JS Library 1.1.1
53514  * Copyright(c) 2006-2007, Ext JS, LLC.
53515  *
53516  * Originally Released Under LGPL - original licence link has changed is not relivant.
53517  *
53518  * Fork - LGPL
53519  * <script type="text/javascript">
53520  */
53521  
53522  
53523 /*
53524  * Private internal class for reading and applying state
53525  */
53526 Roo.LayoutStateManager = function(layout){
53527      // default empty state
53528      this.state = {
53529         north: {},
53530         south: {},
53531         east: {},
53532         west: {}       
53533     };
53534 };
53535
53536 Roo.LayoutStateManager.prototype = {
53537     init : function(layout, provider){
53538         this.provider = provider;
53539         var state = provider.get(layout.id+"-layout-state");
53540         if(state){
53541             var wasUpdating = layout.isUpdating();
53542             if(!wasUpdating){
53543                 layout.beginUpdate();
53544             }
53545             for(var key in state){
53546                 if(typeof state[key] != "function"){
53547                     var rstate = state[key];
53548                     var r = layout.getRegion(key);
53549                     if(r && rstate){
53550                         if(rstate.size){
53551                             r.resizeTo(rstate.size);
53552                         }
53553                         if(rstate.collapsed == true){
53554                             r.collapse(true);
53555                         }else{
53556                             r.expand(null, true);
53557                         }
53558                     }
53559                 }
53560             }
53561             if(!wasUpdating){
53562                 layout.endUpdate();
53563             }
53564             this.state = state; 
53565         }
53566         this.layout = layout;
53567         layout.on("regionresized", this.onRegionResized, this);
53568         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53569         layout.on("regionexpanded", this.onRegionExpanded, this);
53570     },
53571     
53572     storeState : function(){
53573         this.provider.set(this.layout.id+"-layout-state", this.state);
53574     },
53575     
53576     onRegionResized : function(region, newSize){
53577         this.state[region.getPosition()].size = newSize;
53578         this.storeState();
53579     },
53580     
53581     onRegionCollapsed : function(region){
53582         this.state[region.getPosition()].collapsed = true;
53583         this.storeState();
53584     },
53585     
53586     onRegionExpanded : function(region){
53587         this.state[region.getPosition()].collapsed = false;
53588         this.storeState();
53589     }
53590 };/*
53591  * Based on:
53592  * Ext JS Library 1.1.1
53593  * Copyright(c) 2006-2007, Ext JS, LLC.
53594  *
53595  * Originally Released Under LGPL - original licence link has changed is not relivant.
53596  *
53597  * Fork - LGPL
53598  * <script type="text/javascript">
53599  */
53600 /**
53601  * @class Roo.ContentPanel
53602  * @extends Roo.util.Observable
53603  * A basic ContentPanel element.
53604  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53605  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53606  * @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
53607  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53608  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53609  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53610  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53611  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53612  * @cfg {String} title          The title for this panel
53613  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53614  * @cfg {String} url            Calls {@link #setUrl} with this value
53615  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53616  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53617  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53618  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53619
53620  * @constructor
53621  * Create a new ContentPanel.
53622  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53623  * @param {String/Object} config A string to set only the title or a config object
53624  * @param {String} content (optional) Set the HTML content for this panel
53625  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53626  */
53627 Roo.ContentPanel = function(el, config, content){
53628     
53629      
53630     /*
53631     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53632         config = el;
53633         el = Roo.id();
53634     }
53635     if (config && config.parentLayout) { 
53636         el = config.parentLayout.el.createChild(); 
53637     }
53638     */
53639     if(el.autoCreate){ // xtype is available if this is called from factory
53640         config = el;
53641         el = Roo.id();
53642     }
53643     this.el = Roo.get(el);
53644     if(!this.el && config && config.autoCreate){
53645         if(typeof config.autoCreate == "object"){
53646             if(!config.autoCreate.id){
53647                 config.autoCreate.id = config.id||el;
53648             }
53649             this.el = Roo.DomHelper.append(document.body,
53650                         config.autoCreate, true);
53651         }else{
53652             this.el = Roo.DomHelper.append(document.body,
53653                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53654         }
53655     }
53656     this.closable = false;
53657     this.loaded = false;
53658     this.active = false;
53659     if(typeof config == "string"){
53660         this.title = config;
53661     }else{
53662         Roo.apply(this, config);
53663     }
53664     
53665     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53666         this.wrapEl = this.el.wrap();
53667         this.toolbar.container = this.el.insertSibling(false, 'before');
53668         this.toolbar = new Roo.Toolbar(this.toolbar);
53669     }
53670     
53671     // xtype created footer. - not sure if will work as we normally have to render first..
53672     if (this.footer && !this.footer.el && this.footer.xtype) {
53673         if (!this.wrapEl) {
53674             this.wrapEl = this.el.wrap();
53675         }
53676     
53677         this.footer.container = this.wrapEl.createChild();
53678          
53679         this.footer = Roo.factory(this.footer, Roo);
53680         
53681     }
53682     
53683     if(this.resizeEl){
53684         this.resizeEl = Roo.get(this.resizeEl, true);
53685     }else{
53686         this.resizeEl = this.el;
53687     }
53688     // handle view.xtype
53689     
53690  
53691     
53692     
53693     this.addEvents({
53694         /**
53695          * @event activate
53696          * Fires when this panel is activated. 
53697          * @param {Roo.ContentPanel} this
53698          */
53699         "activate" : true,
53700         /**
53701          * @event deactivate
53702          * Fires when this panel is activated. 
53703          * @param {Roo.ContentPanel} this
53704          */
53705         "deactivate" : true,
53706
53707         /**
53708          * @event resize
53709          * Fires when this panel is resized if fitToFrame is true.
53710          * @param {Roo.ContentPanel} this
53711          * @param {Number} width The width after any component adjustments
53712          * @param {Number} height The height after any component adjustments
53713          */
53714         "resize" : true,
53715         
53716          /**
53717          * @event render
53718          * Fires when this tab is created
53719          * @param {Roo.ContentPanel} this
53720          */
53721         "render" : true
53722          
53723         
53724     });
53725     
53726
53727     
53728     
53729     if(this.autoScroll){
53730         this.resizeEl.setStyle("overflow", "auto");
53731     } else {
53732         // fix randome scrolling
53733         this.el.on('scroll', function() {
53734             Roo.log('fix random scolling');
53735             this.scrollTo('top',0); 
53736         });
53737     }
53738     content = content || this.content;
53739     if(content){
53740         this.setContent(content);
53741     }
53742     if(config && config.url){
53743         this.setUrl(this.url, this.params, this.loadOnce);
53744     }
53745     
53746     
53747     
53748     Roo.ContentPanel.superclass.constructor.call(this);
53749     
53750     if (this.view && typeof(this.view.xtype) != 'undefined') {
53751         this.view.el = this.el.appendChild(document.createElement("div"));
53752         this.view = Roo.factory(this.view); 
53753         this.view.render  &&  this.view.render(false, '');  
53754     }
53755     
53756     
53757     this.fireEvent('render', this);
53758 };
53759
53760 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53761     tabTip:'',
53762     setRegion : function(region){
53763         this.region = region;
53764         if(region){
53765            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53766         }else{
53767            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53768         } 
53769     },
53770     
53771     /**
53772      * Returns the toolbar for this Panel if one was configured. 
53773      * @return {Roo.Toolbar} 
53774      */
53775     getToolbar : function(){
53776         return this.toolbar;
53777     },
53778     
53779     setActiveState : function(active){
53780         this.active = active;
53781         if(!active){
53782             this.fireEvent("deactivate", this);
53783         }else{
53784             this.fireEvent("activate", this);
53785         }
53786     },
53787     /**
53788      * Updates this panel's element
53789      * @param {String} content The new content
53790      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53791     */
53792     setContent : function(content, loadScripts){
53793         this.el.update(content, loadScripts);
53794     },
53795
53796     ignoreResize : function(w, h){
53797         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53798             return true;
53799         }else{
53800             this.lastSize = {width: w, height: h};
53801             return false;
53802         }
53803     },
53804     /**
53805      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53806      * @return {Roo.UpdateManager} The UpdateManager
53807      */
53808     getUpdateManager : function(){
53809         return this.el.getUpdateManager();
53810     },
53811      /**
53812      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53813      * @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:
53814 <pre><code>
53815 panel.load({
53816     url: "your-url.php",
53817     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53818     callback: yourFunction,
53819     scope: yourObject, //(optional scope)
53820     discardUrl: false,
53821     nocache: false,
53822     text: "Loading...",
53823     timeout: 30,
53824     scripts: false
53825 });
53826 </code></pre>
53827      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53828      * 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.
53829      * @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}
53830      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53831      * @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.
53832      * @return {Roo.ContentPanel} this
53833      */
53834     load : function(){
53835         var um = this.el.getUpdateManager();
53836         um.update.apply(um, arguments);
53837         return this;
53838     },
53839
53840
53841     /**
53842      * 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.
53843      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53844      * @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)
53845      * @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)
53846      * @return {Roo.UpdateManager} The UpdateManager
53847      */
53848     setUrl : function(url, params, loadOnce){
53849         if(this.refreshDelegate){
53850             this.removeListener("activate", this.refreshDelegate);
53851         }
53852         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53853         this.on("activate", this.refreshDelegate);
53854         return this.el.getUpdateManager();
53855     },
53856     
53857     _handleRefresh : function(url, params, loadOnce){
53858         if(!loadOnce || !this.loaded){
53859             var updater = this.el.getUpdateManager();
53860             updater.update(url, params, this._setLoaded.createDelegate(this));
53861         }
53862     },
53863     
53864     _setLoaded : function(){
53865         this.loaded = true;
53866     }, 
53867     
53868     /**
53869      * Returns this panel's id
53870      * @return {String} 
53871      */
53872     getId : function(){
53873         return this.el.id;
53874     },
53875     
53876     /** 
53877      * Returns this panel's element - used by regiosn to add.
53878      * @return {Roo.Element} 
53879      */
53880     getEl : function(){
53881         return this.wrapEl || this.el;
53882     },
53883     
53884     adjustForComponents : function(width, height)
53885     {
53886         //Roo.log('adjustForComponents ');
53887         if(this.resizeEl != this.el){
53888             width -= this.el.getFrameWidth('lr');
53889             height -= this.el.getFrameWidth('tb');
53890         }
53891         if(this.toolbar){
53892             var te = this.toolbar.getEl();
53893             height -= te.getHeight();
53894             te.setWidth(width);
53895         }
53896         if(this.footer){
53897             var te = this.footer.getEl();
53898             //Roo.log("footer:" + te.getHeight());
53899             
53900             height -= te.getHeight();
53901             te.setWidth(width);
53902         }
53903         
53904         
53905         if(this.adjustments){
53906             width += this.adjustments[0];
53907             height += this.adjustments[1];
53908         }
53909         return {"width": width, "height": height};
53910     },
53911     
53912     setSize : function(width, height){
53913         if(this.fitToFrame && !this.ignoreResize(width, height)){
53914             if(this.fitContainer && this.resizeEl != this.el){
53915                 this.el.setSize(width, height);
53916             }
53917             var size = this.adjustForComponents(width, height);
53918             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53919             this.fireEvent('resize', this, size.width, size.height);
53920         }
53921     },
53922     
53923     /**
53924      * Returns this panel's title
53925      * @return {String} 
53926      */
53927     getTitle : function(){
53928         return this.title;
53929     },
53930     
53931     /**
53932      * Set this panel's title
53933      * @param {String} title
53934      */
53935     setTitle : function(title){
53936         this.title = title;
53937         if(this.region){
53938             this.region.updatePanelTitle(this, title);
53939         }
53940     },
53941     
53942     /**
53943      * Returns true is this panel was configured to be closable
53944      * @return {Boolean} 
53945      */
53946     isClosable : function(){
53947         return this.closable;
53948     },
53949     
53950     beforeSlide : function(){
53951         this.el.clip();
53952         this.resizeEl.clip();
53953     },
53954     
53955     afterSlide : function(){
53956         this.el.unclip();
53957         this.resizeEl.unclip();
53958     },
53959     
53960     /**
53961      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53962      *   Will fail silently if the {@link #setUrl} method has not been called.
53963      *   This does not activate the panel, just updates its content.
53964      */
53965     refresh : function(){
53966         if(this.refreshDelegate){
53967            this.loaded = false;
53968            this.refreshDelegate();
53969         }
53970     },
53971     
53972     /**
53973      * Destroys this panel
53974      */
53975     destroy : function(){
53976         this.el.removeAllListeners();
53977         var tempEl = document.createElement("span");
53978         tempEl.appendChild(this.el.dom);
53979         tempEl.innerHTML = "";
53980         this.el.remove();
53981         this.el = null;
53982     },
53983     
53984     /**
53985      * form - if the content panel contains a form - this is a reference to it.
53986      * @type {Roo.form.Form}
53987      */
53988     form : false,
53989     /**
53990      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53991      *    This contains a reference to it.
53992      * @type {Roo.View}
53993      */
53994     view : false,
53995     
53996       /**
53997      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53998      * <pre><code>
53999
54000 layout.addxtype({
54001        xtype : 'Form',
54002        items: [ .... ]
54003    }
54004 );
54005
54006 </code></pre>
54007      * @param {Object} cfg Xtype definition of item to add.
54008      */
54009     
54010     addxtype : function(cfg) {
54011         // add form..
54012         if (cfg.xtype.match(/^Form$/)) {
54013             
54014             var el;
54015             //if (this.footer) {
54016             //    el = this.footer.container.insertSibling(false, 'before');
54017             //} else {
54018                 el = this.el.createChild();
54019             //}
54020
54021             this.form = new  Roo.form.Form(cfg);
54022             
54023             
54024             if ( this.form.allItems.length) {
54025                 this.form.render(el.dom);
54026             }
54027             return this.form;
54028         }
54029         // should only have one of theses..
54030         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54031             // views.. should not be just added - used named prop 'view''
54032             
54033             cfg.el = this.el.appendChild(document.createElement("div"));
54034             // factory?
54035             
54036             var ret = new Roo.factory(cfg);
54037              
54038              ret.render && ret.render(false, ''); // render blank..
54039             this.view = ret;
54040             return ret;
54041         }
54042         return false;
54043     }
54044 });
54045
54046 /**
54047  * @class Roo.GridPanel
54048  * @extends Roo.ContentPanel
54049  * @constructor
54050  * Create a new GridPanel.
54051  * @param {Roo.grid.Grid} grid The grid for this panel
54052  * @param {String/Object} config A string to set only the panel's title, or a config object
54053  */
54054 Roo.GridPanel = function(grid, config){
54055     
54056   
54057     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54058         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54059         
54060     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54061     
54062     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54063     
54064     if(this.toolbar){
54065         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54066     }
54067     // xtype created footer. - not sure if will work as we normally have to render first..
54068     if (this.footer && !this.footer.el && this.footer.xtype) {
54069         
54070         this.footer.container = this.grid.getView().getFooterPanel(true);
54071         this.footer.dataSource = this.grid.dataSource;
54072         this.footer = Roo.factory(this.footer, Roo);
54073         
54074     }
54075     
54076     grid.monitorWindowResize = false; // turn off autosizing
54077     grid.autoHeight = false;
54078     grid.autoWidth = false;
54079     this.grid = grid;
54080     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54081 };
54082
54083 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54084     getId : function(){
54085         return this.grid.id;
54086     },
54087     
54088     /**
54089      * Returns the grid for this panel
54090      * @return {Roo.grid.Grid} 
54091      */
54092     getGrid : function(){
54093         return this.grid;    
54094     },
54095     
54096     setSize : function(width, height){
54097         if(!this.ignoreResize(width, height)){
54098             var grid = this.grid;
54099             var size = this.adjustForComponents(width, height);
54100             grid.getGridEl().setSize(size.width, size.height);
54101             grid.autoSize();
54102         }
54103     },
54104     
54105     beforeSlide : function(){
54106         this.grid.getView().scroller.clip();
54107     },
54108     
54109     afterSlide : function(){
54110         this.grid.getView().scroller.unclip();
54111     },
54112     
54113     destroy : function(){
54114         this.grid.destroy();
54115         delete this.grid;
54116         Roo.GridPanel.superclass.destroy.call(this); 
54117     }
54118 });
54119
54120
54121 /**
54122  * @class Roo.NestedLayoutPanel
54123  * @extends Roo.ContentPanel
54124  * @constructor
54125  * Create a new NestedLayoutPanel.
54126  * 
54127  * 
54128  * @param {Roo.BorderLayout} layout The layout for this panel
54129  * @param {String/Object} config A string to set only the title or a config object
54130  */
54131 Roo.NestedLayoutPanel = function(layout, config)
54132 {
54133     // construct with only one argument..
54134     /* FIXME - implement nicer consturctors
54135     if (layout.layout) {
54136         config = layout;
54137         layout = config.layout;
54138         delete config.layout;
54139     }
54140     if (layout.xtype && !layout.getEl) {
54141         // then layout needs constructing..
54142         layout = Roo.factory(layout, Roo);
54143     }
54144     */
54145     
54146     
54147     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54148     
54149     layout.monitorWindowResize = false; // turn off autosizing
54150     this.layout = layout;
54151     this.layout.getEl().addClass("x-layout-nested-layout");
54152     
54153     
54154     
54155     
54156 };
54157
54158 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54159
54160     setSize : function(width, height){
54161         if(!this.ignoreResize(width, height)){
54162             var size = this.adjustForComponents(width, height);
54163             var el = this.layout.getEl();
54164             el.setSize(size.width, size.height);
54165             var touch = el.dom.offsetWidth;
54166             this.layout.layout();
54167             // ie requires a double layout on the first pass
54168             if(Roo.isIE && !this.initialized){
54169                 this.initialized = true;
54170                 this.layout.layout();
54171             }
54172         }
54173     },
54174     
54175     // activate all subpanels if not currently active..
54176     
54177     setActiveState : function(active){
54178         this.active = active;
54179         if(!active){
54180             this.fireEvent("deactivate", this);
54181             return;
54182         }
54183         
54184         this.fireEvent("activate", this);
54185         // not sure if this should happen before or after..
54186         if (!this.layout) {
54187             return; // should not happen..
54188         }
54189         var reg = false;
54190         for (var r in this.layout.regions) {
54191             reg = this.layout.getRegion(r);
54192             if (reg.getActivePanel()) {
54193                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54194                 reg.setActivePanel(reg.getActivePanel());
54195                 continue;
54196             }
54197             if (!reg.panels.length) {
54198                 continue;
54199             }
54200             reg.showPanel(reg.getPanel(0));
54201         }
54202         
54203         
54204         
54205         
54206     },
54207     
54208     /**
54209      * Returns the nested BorderLayout for this panel
54210      * @return {Roo.BorderLayout} 
54211      */
54212     getLayout : function(){
54213         return this.layout;
54214     },
54215     
54216      /**
54217      * Adds a xtype elements to the layout of the nested panel
54218      * <pre><code>
54219
54220 panel.addxtype({
54221        xtype : 'ContentPanel',
54222        region: 'west',
54223        items: [ .... ]
54224    }
54225 );
54226
54227 panel.addxtype({
54228         xtype : 'NestedLayoutPanel',
54229         region: 'west',
54230         layout: {
54231            center: { },
54232            west: { }   
54233         },
54234         items : [ ... list of content panels or nested layout panels.. ]
54235    }
54236 );
54237 </code></pre>
54238      * @param {Object} cfg Xtype definition of item to add.
54239      */
54240     addxtype : function(cfg) {
54241         return this.layout.addxtype(cfg);
54242     
54243     }
54244 });
54245
54246 Roo.ScrollPanel = function(el, config, content){
54247     config = config || {};
54248     config.fitToFrame = true;
54249     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54250     
54251     this.el.dom.style.overflow = "hidden";
54252     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54253     this.el.removeClass("x-layout-inactive-content");
54254     this.el.on("mousewheel", this.onWheel, this);
54255
54256     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54257     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54258     up.unselectable(); down.unselectable();
54259     up.on("click", this.scrollUp, this);
54260     down.on("click", this.scrollDown, this);
54261     up.addClassOnOver("x-scroller-btn-over");
54262     down.addClassOnOver("x-scroller-btn-over");
54263     up.addClassOnClick("x-scroller-btn-click");
54264     down.addClassOnClick("x-scroller-btn-click");
54265     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54266
54267     this.resizeEl = this.el;
54268     this.el = wrap; this.up = up; this.down = down;
54269 };
54270
54271 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54272     increment : 100,
54273     wheelIncrement : 5,
54274     scrollUp : function(){
54275         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54276     },
54277
54278     scrollDown : function(){
54279         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54280     },
54281
54282     afterScroll : function(){
54283         var el = this.resizeEl;
54284         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54285         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54286         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54287     },
54288
54289     setSize : function(){
54290         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54291         this.afterScroll();
54292     },
54293
54294     onWheel : function(e){
54295         var d = e.getWheelDelta();
54296         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54297         this.afterScroll();
54298         e.stopEvent();
54299     },
54300
54301     setContent : function(content, loadScripts){
54302         this.resizeEl.update(content, loadScripts);
54303     }
54304
54305 });
54306
54307
54308
54309
54310
54311
54312
54313
54314
54315 /**
54316  * @class Roo.TreePanel
54317  * @extends Roo.ContentPanel
54318  * @constructor
54319  * Create a new TreePanel. - defaults to fit/scoll contents.
54320  * @param {String/Object} config A string to set only the panel's title, or a config object
54321  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54322  */
54323 Roo.TreePanel = function(config){
54324     var el = config.el;
54325     var tree = config.tree;
54326     delete config.tree; 
54327     delete config.el; // hopefull!
54328     
54329     // wrapper for IE7 strict & safari scroll issue
54330     
54331     var treeEl = el.createChild();
54332     config.resizeEl = treeEl;
54333     
54334     
54335     
54336     Roo.TreePanel.superclass.constructor.call(this, el, config);
54337  
54338  
54339     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54340     //console.log(tree);
54341     this.on('activate', function()
54342     {
54343         if (this.tree.rendered) {
54344             return;
54345         }
54346         //console.log('render tree');
54347         this.tree.render();
54348     });
54349     // this should not be needed.. - it's actually the 'el' that resizes?
54350     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54351     
54352     //this.on('resize',  function (cp, w, h) {
54353     //        this.tree.innerCt.setWidth(w);
54354     //        this.tree.innerCt.setHeight(h);
54355     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54356     //});
54357
54358         
54359     
54360 };
54361
54362 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54363     fitToFrame : true,
54364     autoScroll : true
54365 });
54366
54367
54368
54369
54370
54371
54372
54373
54374
54375
54376
54377 /*
54378  * Based on:
54379  * Ext JS Library 1.1.1
54380  * Copyright(c) 2006-2007, Ext JS, LLC.
54381  *
54382  * Originally Released Under LGPL - original licence link has changed is not relivant.
54383  *
54384  * Fork - LGPL
54385  * <script type="text/javascript">
54386  */
54387  
54388
54389 /**
54390  * @class Roo.ReaderLayout
54391  * @extends Roo.BorderLayout
54392  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54393  * center region containing two nested regions (a top one for a list view and one for item preview below),
54394  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54395  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54396  * expedites the setup of the overall layout and regions for this common application style.
54397  * Example:
54398  <pre><code>
54399 var reader = new Roo.ReaderLayout();
54400 var CP = Roo.ContentPanel;  // shortcut for adding
54401
54402 reader.beginUpdate();
54403 reader.add("north", new CP("north", "North"));
54404 reader.add("west", new CP("west", {title: "West"}));
54405 reader.add("east", new CP("east", {title: "East"}));
54406
54407 reader.regions.listView.add(new CP("listView", "List"));
54408 reader.regions.preview.add(new CP("preview", "Preview"));
54409 reader.endUpdate();
54410 </code></pre>
54411 * @constructor
54412 * Create a new ReaderLayout
54413 * @param {Object} config Configuration options
54414 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54415 * document.body if omitted)
54416 */
54417 Roo.ReaderLayout = function(config, renderTo){
54418     var c = config || {size:{}};
54419     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54420         north: c.north !== false ? Roo.apply({
54421             split:false,
54422             initialSize: 32,
54423             titlebar: false
54424         }, c.north) : false,
54425         west: c.west !== false ? Roo.apply({
54426             split:true,
54427             initialSize: 200,
54428             minSize: 175,
54429             maxSize: 400,
54430             titlebar: true,
54431             collapsible: true,
54432             animate: true,
54433             margins:{left:5,right:0,bottom:5,top:5},
54434             cmargins:{left:5,right:5,bottom:5,top:5}
54435         }, c.west) : false,
54436         east: c.east !== false ? Roo.apply({
54437             split:true,
54438             initialSize: 200,
54439             minSize: 175,
54440             maxSize: 400,
54441             titlebar: true,
54442             collapsible: true,
54443             animate: true,
54444             margins:{left:0,right:5,bottom:5,top:5},
54445             cmargins:{left:5,right:5,bottom:5,top:5}
54446         }, c.east) : false,
54447         center: Roo.apply({
54448             tabPosition: 'top',
54449             autoScroll:false,
54450             closeOnTab: true,
54451             titlebar:false,
54452             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54453         }, c.center)
54454     });
54455
54456     this.el.addClass('x-reader');
54457
54458     this.beginUpdate();
54459
54460     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54461         south: c.preview !== false ? Roo.apply({
54462             split:true,
54463             initialSize: 200,
54464             minSize: 100,
54465             autoScroll:true,
54466             collapsible:true,
54467             titlebar: true,
54468             cmargins:{top:5,left:0, right:0, bottom:0}
54469         }, c.preview) : false,
54470         center: Roo.apply({
54471             autoScroll:false,
54472             titlebar:false,
54473             minHeight:200
54474         }, c.listView)
54475     });
54476     this.add('center', new Roo.NestedLayoutPanel(inner,
54477             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54478
54479     this.endUpdate();
54480
54481     this.regions.preview = inner.getRegion('south');
54482     this.regions.listView = inner.getRegion('center');
54483 };
54484
54485 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54486  * Based on:
54487  * Ext JS Library 1.1.1
54488  * Copyright(c) 2006-2007, Ext JS, LLC.
54489  *
54490  * Originally Released Under LGPL - original licence link has changed is not relivant.
54491  *
54492  * Fork - LGPL
54493  * <script type="text/javascript">
54494  */
54495  
54496 /**
54497  * @class Roo.grid.Grid
54498  * @extends Roo.util.Observable
54499  * This class represents the primary interface of a component based grid control.
54500  * <br><br>Usage:<pre><code>
54501  var grid = new Roo.grid.Grid("my-container-id", {
54502      ds: myDataStore,
54503      cm: myColModel,
54504      selModel: mySelectionModel,
54505      autoSizeColumns: true,
54506      monitorWindowResize: false,
54507      trackMouseOver: true
54508  });
54509  // set any options
54510  grid.render();
54511  * </code></pre>
54512  * <b>Common Problems:</b><br/>
54513  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54514  * element will correct this<br/>
54515  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54516  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54517  * are unpredictable.<br/>
54518  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54519  * grid to calculate dimensions/offsets.<br/>
54520   * @constructor
54521  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54522  * The container MUST have some type of size defined for the grid to fill. The container will be
54523  * automatically set to position relative if it isn't already.
54524  * @param {Object} config A config object that sets properties on this grid.
54525  */
54526 Roo.grid.Grid = function(container, config){
54527         // initialize the container
54528         this.container = Roo.get(container);
54529         this.container.update("");
54530         this.container.setStyle("overflow", "hidden");
54531     this.container.addClass('x-grid-container');
54532
54533     this.id = this.container.id;
54534
54535     Roo.apply(this, config);
54536     // check and correct shorthanded configs
54537     if(this.ds){
54538         this.dataSource = this.ds;
54539         delete this.ds;
54540     }
54541     if(this.cm){
54542         this.colModel = this.cm;
54543         delete this.cm;
54544     }
54545     if(this.sm){
54546         this.selModel = this.sm;
54547         delete this.sm;
54548     }
54549
54550     if (this.selModel) {
54551         this.selModel = Roo.factory(this.selModel, Roo.grid);
54552         this.sm = this.selModel;
54553         this.sm.xmodule = this.xmodule || false;
54554     }
54555     if (typeof(this.colModel.config) == 'undefined') {
54556         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54557         this.cm = this.colModel;
54558         this.cm.xmodule = this.xmodule || false;
54559     }
54560     if (this.dataSource) {
54561         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54562         this.ds = this.dataSource;
54563         this.ds.xmodule = this.xmodule || false;
54564          
54565     }
54566     
54567     
54568     
54569     if(this.width){
54570         this.container.setWidth(this.width);
54571     }
54572
54573     if(this.height){
54574         this.container.setHeight(this.height);
54575     }
54576     /** @private */
54577         this.addEvents({
54578         // raw events
54579         /**
54580          * @event click
54581          * The raw click event for the entire grid.
54582          * @param {Roo.EventObject} e
54583          */
54584         "click" : true,
54585         /**
54586          * @event dblclick
54587          * The raw dblclick event for the entire grid.
54588          * @param {Roo.EventObject} e
54589          */
54590         "dblclick" : true,
54591         /**
54592          * @event contextmenu
54593          * The raw contextmenu event for the entire grid.
54594          * @param {Roo.EventObject} e
54595          */
54596         "contextmenu" : true,
54597         /**
54598          * @event mousedown
54599          * The raw mousedown event for the entire grid.
54600          * @param {Roo.EventObject} e
54601          */
54602         "mousedown" : true,
54603         /**
54604          * @event mouseup
54605          * The raw mouseup event for the entire grid.
54606          * @param {Roo.EventObject} e
54607          */
54608         "mouseup" : true,
54609         /**
54610          * @event mouseover
54611          * The raw mouseover event for the entire grid.
54612          * @param {Roo.EventObject} e
54613          */
54614         "mouseover" : true,
54615         /**
54616          * @event mouseout
54617          * The raw mouseout event for the entire grid.
54618          * @param {Roo.EventObject} e
54619          */
54620         "mouseout" : true,
54621         /**
54622          * @event keypress
54623          * The raw keypress event for the entire grid.
54624          * @param {Roo.EventObject} e
54625          */
54626         "keypress" : true,
54627         /**
54628          * @event keydown
54629          * The raw keydown event for the entire grid.
54630          * @param {Roo.EventObject} e
54631          */
54632         "keydown" : true,
54633
54634         // custom events
54635
54636         /**
54637          * @event cellclick
54638          * Fires when a cell is clicked
54639          * @param {Grid} this
54640          * @param {Number} rowIndex
54641          * @param {Number} columnIndex
54642          * @param {Roo.EventObject} e
54643          */
54644         "cellclick" : true,
54645         /**
54646          * @event celldblclick
54647          * Fires when a cell is double clicked
54648          * @param {Grid} this
54649          * @param {Number} rowIndex
54650          * @param {Number} columnIndex
54651          * @param {Roo.EventObject} e
54652          */
54653         "celldblclick" : true,
54654         /**
54655          * @event rowclick
54656          * Fires when a row is clicked
54657          * @param {Grid} this
54658          * @param {Number} rowIndex
54659          * @param {Roo.EventObject} e
54660          */
54661         "rowclick" : true,
54662         /**
54663          * @event rowdblclick
54664          * Fires when a row is double clicked
54665          * @param {Grid} this
54666          * @param {Number} rowIndex
54667          * @param {Roo.EventObject} e
54668          */
54669         "rowdblclick" : true,
54670         /**
54671          * @event headerclick
54672          * Fires when a header is clicked
54673          * @param {Grid} this
54674          * @param {Number} columnIndex
54675          * @param {Roo.EventObject} e
54676          */
54677         "headerclick" : true,
54678         /**
54679          * @event headerdblclick
54680          * Fires when a header cell is double clicked
54681          * @param {Grid} this
54682          * @param {Number} columnIndex
54683          * @param {Roo.EventObject} e
54684          */
54685         "headerdblclick" : true,
54686         /**
54687          * @event rowcontextmenu
54688          * Fires when a row is right clicked
54689          * @param {Grid} this
54690          * @param {Number} rowIndex
54691          * @param {Roo.EventObject} e
54692          */
54693         "rowcontextmenu" : true,
54694         /**
54695          * @event cellcontextmenu
54696          * Fires when a cell is right clicked
54697          * @param {Grid} this
54698          * @param {Number} rowIndex
54699          * @param {Number} cellIndex
54700          * @param {Roo.EventObject} e
54701          */
54702          "cellcontextmenu" : true,
54703         /**
54704          * @event headercontextmenu
54705          * Fires when a header is right clicked
54706          * @param {Grid} this
54707          * @param {Number} columnIndex
54708          * @param {Roo.EventObject} e
54709          */
54710         "headercontextmenu" : true,
54711         /**
54712          * @event bodyscroll
54713          * Fires when the body element is scrolled
54714          * @param {Number} scrollLeft
54715          * @param {Number} scrollTop
54716          */
54717         "bodyscroll" : true,
54718         /**
54719          * @event columnresize
54720          * Fires when the user resizes a column
54721          * @param {Number} columnIndex
54722          * @param {Number} newSize
54723          */
54724         "columnresize" : true,
54725         /**
54726          * @event columnmove
54727          * Fires when the user moves a column
54728          * @param {Number} oldIndex
54729          * @param {Number} newIndex
54730          */
54731         "columnmove" : true,
54732         /**
54733          * @event startdrag
54734          * Fires when row(s) start being dragged
54735          * @param {Grid} this
54736          * @param {Roo.GridDD} dd The drag drop object
54737          * @param {event} e The raw browser event
54738          */
54739         "startdrag" : true,
54740         /**
54741          * @event enddrag
54742          * Fires when a drag operation is complete
54743          * @param {Grid} this
54744          * @param {Roo.GridDD} dd The drag drop object
54745          * @param {event} e The raw browser event
54746          */
54747         "enddrag" : true,
54748         /**
54749          * @event dragdrop
54750          * Fires when dragged row(s) are dropped on a valid DD target
54751          * @param {Grid} this
54752          * @param {Roo.GridDD} dd The drag drop object
54753          * @param {String} targetId The target drag drop object
54754          * @param {event} e The raw browser event
54755          */
54756         "dragdrop" : true,
54757         /**
54758          * @event dragover
54759          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54760          * @param {Grid} this
54761          * @param {Roo.GridDD} dd The drag drop object
54762          * @param {String} targetId The target drag drop object
54763          * @param {event} e The raw browser event
54764          */
54765         "dragover" : true,
54766         /**
54767          * @event dragenter
54768          *  Fires when the dragged row(s) first cross another DD target while being dragged
54769          * @param {Grid} this
54770          * @param {Roo.GridDD} dd The drag drop object
54771          * @param {String} targetId The target drag drop object
54772          * @param {event} e The raw browser event
54773          */
54774         "dragenter" : true,
54775         /**
54776          * @event dragout
54777          * Fires when the dragged row(s) leave another DD target while being dragged
54778          * @param {Grid} this
54779          * @param {Roo.GridDD} dd The drag drop object
54780          * @param {String} targetId The target drag drop object
54781          * @param {event} e The raw browser event
54782          */
54783         "dragout" : true,
54784         /**
54785          * @event rowclass
54786          * Fires when a row is rendered, so you can change add a style to it.
54787          * @param {GridView} gridview   The grid view
54788          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54789          */
54790         'rowclass' : true,
54791
54792         /**
54793          * @event render
54794          * Fires when the grid is rendered
54795          * @param {Grid} grid
54796          */
54797         'render' : true
54798     });
54799
54800     Roo.grid.Grid.superclass.constructor.call(this);
54801 };
54802 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54803     
54804     /**
54805      * @cfg {String} ddGroup - drag drop group.
54806      */
54807
54808     /**
54809      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54810      */
54811     minColumnWidth : 25,
54812
54813     /**
54814      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54815      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54816      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54817      */
54818     autoSizeColumns : false,
54819
54820     /**
54821      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54822      */
54823     autoSizeHeaders : true,
54824
54825     /**
54826      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54827      */
54828     monitorWindowResize : true,
54829
54830     /**
54831      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54832      * rows measured to get a columns size. Default is 0 (all rows).
54833      */
54834     maxRowsToMeasure : 0,
54835
54836     /**
54837      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54838      */
54839     trackMouseOver : true,
54840
54841     /**
54842     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54843     */
54844     
54845     /**
54846     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54847     */
54848     enableDragDrop : false,
54849     
54850     /**
54851     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54852     */
54853     enableColumnMove : true,
54854     
54855     /**
54856     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54857     */
54858     enableColumnHide : true,
54859     
54860     /**
54861     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54862     */
54863     enableRowHeightSync : false,
54864     
54865     /**
54866     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54867     */
54868     stripeRows : true,
54869     
54870     /**
54871     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54872     */
54873     autoHeight : false,
54874
54875     /**
54876      * @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.
54877      */
54878     autoExpandColumn : false,
54879
54880     /**
54881     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54882     * Default is 50.
54883     */
54884     autoExpandMin : 50,
54885
54886     /**
54887     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54888     */
54889     autoExpandMax : 1000,
54890
54891     /**
54892     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54893     */
54894     view : null,
54895
54896     /**
54897     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54898     */
54899     loadMask : false,
54900     /**
54901     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54902     */
54903     dropTarget: false,
54904     
54905    
54906     
54907     // private
54908     rendered : false,
54909
54910     /**
54911     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54912     * of a fixed width. Default is false.
54913     */
54914     /**
54915     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54916     */
54917     /**
54918      * Called once after all setup has been completed and the grid is ready to be rendered.
54919      * @return {Roo.grid.Grid} this
54920      */
54921     render : function()
54922     {
54923         var c = this.container;
54924         // try to detect autoHeight/width mode
54925         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54926             this.autoHeight = true;
54927         }
54928         var view = this.getView();
54929         view.init(this);
54930
54931         c.on("click", this.onClick, this);
54932         c.on("dblclick", this.onDblClick, this);
54933         c.on("contextmenu", this.onContextMenu, this);
54934         c.on("keydown", this.onKeyDown, this);
54935         if (Roo.isTouch) {
54936             c.on("touchstart", this.onTouchStart, this);
54937         }
54938
54939         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54940
54941         this.getSelectionModel().init(this);
54942
54943         view.render();
54944
54945         if(this.loadMask){
54946             this.loadMask = new Roo.LoadMask(this.container,
54947                     Roo.apply({store:this.dataSource}, this.loadMask));
54948         }
54949         
54950         
54951         if (this.toolbar && this.toolbar.xtype) {
54952             this.toolbar.container = this.getView().getHeaderPanel(true);
54953             this.toolbar = new Roo.Toolbar(this.toolbar);
54954         }
54955         if (this.footer && this.footer.xtype) {
54956             this.footer.dataSource = this.getDataSource();
54957             this.footer.container = this.getView().getFooterPanel(true);
54958             this.footer = Roo.factory(this.footer, Roo);
54959         }
54960         if (this.dropTarget && this.dropTarget.xtype) {
54961             delete this.dropTarget.xtype;
54962             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54963         }
54964         
54965         
54966         this.rendered = true;
54967         this.fireEvent('render', this);
54968         return this;
54969     },
54970
54971         /**
54972          * Reconfigures the grid to use a different Store and Column Model.
54973          * The View will be bound to the new objects and refreshed.
54974          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54975          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54976          */
54977     reconfigure : function(dataSource, colModel){
54978         if(this.loadMask){
54979             this.loadMask.destroy();
54980             this.loadMask = new Roo.LoadMask(this.container,
54981                     Roo.apply({store:dataSource}, this.loadMask));
54982         }
54983         this.view.bind(dataSource, colModel);
54984         this.dataSource = dataSource;
54985         this.colModel = colModel;
54986         this.view.refresh(true);
54987     },
54988
54989     // private
54990     onKeyDown : function(e){
54991         this.fireEvent("keydown", e);
54992     },
54993
54994     /**
54995      * Destroy this grid.
54996      * @param {Boolean} removeEl True to remove the element
54997      */
54998     destroy : function(removeEl, keepListeners){
54999         if(this.loadMask){
55000             this.loadMask.destroy();
55001         }
55002         var c = this.container;
55003         c.removeAllListeners();
55004         this.view.destroy();
55005         this.colModel.purgeListeners();
55006         if(!keepListeners){
55007             this.purgeListeners();
55008         }
55009         c.update("");
55010         if(removeEl === true){
55011             c.remove();
55012         }
55013     },
55014
55015     // private
55016     processEvent : function(name, e){
55017         // does this fire select???
55018         //Roo.log('grid:processEvent '  + name);
55019         
55020         if (name != 'touchstart' ) {
55021             this.fireEvent(name, e);    
55022         }
55023         
55024         var t = e.getTarget();
55025         var v = this.view;
55026         var header = v.findHeaderIndex(t);
55027         if(header !== false){
55028             var ename = name == 'touchstart' ? 'click' : name;
55029              
55030             this.fireEvent("header" + ename, this, header, e);
55031         }else{
55032             var row = v.findRowIndex(t);
55033             var cell = v.findCellIndex(t);
55034             if (name == 'touchstart') {
55035                 // first touch is always a click.
55036                 // hopefull this happens after selection is updated.?
55037                 name = false;
55038                 
55039                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55040                     var cs = this.selModel.getSelectedCell();
55041                     if (row == cs[0] && cell == cs[1]){
55042                         name = 'dblclick';
55043                     }
55044                 }
55045                 if (typeof(this.selModel.getSelections) != 'undefined') {
55046                     var cs = this.selModel.getSelections();
55047                     var ds = this.dataSource;
55048                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55049                         name = 'dblclick';
55050                     }
55051                 }
55052                 if (!name) {
55053                     return;
55054                 }
55055             }
55056             
55057             
55058             if(row !== false){
55059                 this.fireEvent("row" + name, this, row, e);
55060                 if(cell !== false){
55061                     this.fireEvent("cell" + name, this, row, cell, e);
55062                 }
55063             }
55064         }
55065     },
55066
55067     // private
55068     onClick : function(e){
55069         this.processEvent("click", e);
55070     },
55071    // private
55072     onTouchStart : function(e){
55073         this.processEvent("touchstart", e);
55074     },
55075
55076     // private
55077     onContextMenu : function(e, t){
55078         this.processEvent("contextmenu", e);
55079     },
55080
55081     // private
55082     onDblClick : function(e){
55083         this.processEvent("dblclick", e);
55084     },
55085
55086     // private
55087     walkCells : function(row, col, step, fn, scope){
55088         var cm = this.colModel, clen = cm.getColumnCount();
55089         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55090         if(step < 0){
55091             if(col < 0){
55092                 row--;
55093                 first = false;
55094             }
55095             while(row >= 0){
55096                 if(!first){
55097                     col = clen-1;
55098                 }
55099                 first = false;
55100                 while(col >= 0){
55101                     if(fn.call(scope || this, row, col, cm) === true){
55102                         return [row, col];
55103                     }
55104                     col--;
55105                 }
55106                 row--;
55107             }
55108         } else {
55109             if(col >= clen){
55110                 row++;
55111                 first = false;
55112             }
55113             while(row < rlen){
55114                 if(!first){
55115                     col = 0;
55116                 }
55117                 first = false;
55118                 while(col < clen){
55119                     if(fn.call(scope || this, row, col, cm) === true){
55120                         return [row, col];
55121                     }
55122                     col++;
55123                 }
55124                 row++;
55125             }
55126         }
55127         return null;
55128     },
55129
55130     // private
55131     getSelections : function(){
55132         return this.selModel.getSelections();
55133     },
55134
55135     /**
55136      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55137      * but if manual update is required this method will initiate it.
55138      */
55139     autoSize : function(){
55140         if(this.rendered){
55141             this.view.layout();
55142             if(this.view.adjustForScroll){
55143                 this.view.adjustForScroll();
55144             }
55145         }
55146     },
55147
55148     /**
55149      * Returns the grid's underlying element.
55150      * @return {Element} The element
55151      */
55152     getGridEl : function(){
55153         return this.container;
55154     },
55155
55156     // private for compatibility, overridden by editor grid
55157     stopEditing : function(){},
55158
55159     /**
55160      * Returns the grid's SelectionModel.
55161      * @return {SelectionModel}
55162      */
55163     getSelectionModel : function(){
55164         if(!this.selModel){
55165             this.selModel = new Roo.grid.RowSelectionModel();
55166         }
55167         return this.selModel;
55168     },
55169
55170     /**
55171      * Returns the grid's DataSource.
55172      * @return {DataSource}
55173      */
55174     getDataSource : function(){
55175         return this.dataSource;
55176     },
55177
55178     /**
55179      * Returns the grid's ColumnModel.
55180      * @return {ColumnModel}
55181      */
55182     getColumnModel : function(){
55183         return this.colModel;
55184     },
55185
55186     /**
55187      * Returns the grid's GridView object.
55188      * @return {GridView}
55189      */
55190     getView : function(){
55191         if(!this.view){
55192             this.view = new Roo.grid.GridView(this.viewConfig);
55193         }
55194         return this.view;
55195     },
55196     /**
55197      * Called to get grid's drag proxy text, by default returns this.ddText.
55198      * @return {String}
55199      */
55200     getDragDropText : function(){
55201         var count = this.selModel.getCount();
55202         return String.format(this.ddText, count, count == 1 ? '' : 's');
55203     }
55204 });
55205 /**
55206  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55207  * %0 is replaced with the number of selected rows.
55208  * @type String
55209  */
55210 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55211  * Based on:
55212  * Ext JS Library 1.1.1
55213  * Copyright(c) 2006-2007, Ext JS, LLC.
55214  *
55215  * Originally Released Under LGPL - original licence link has changed is not relivant.
55216  *
55217  * Fork - LGPL
55218  * <script type="text/javascript">
55219  */
55220  
55221 Roo.grid.AbstractGridView = function(){
55222         this.grid = null;
55223         
55224         this.events = {
55225             "beforerowremoved" : true,
55226             "beforerowsinserted" : true,
55227             "beforerefresh" : true,
55228             "rowremoved" : true,
55229             "rowsinserted" : true,
55230             "rowupdated" : true,
55231             "refresh" : true
55232         };
55233     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55234 };
55235
55236 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55237     rowClass : "x-grid-row",
55238     cellClass : "x-grid-cell",
55239     tdClass : "x-grid-td",
55240     hdClass : "x-grid-hd",
55241     splitClass : "x-grid-hd-split",
55242     
55243     init: function(grid){
55244         this.grid = grid;
55245                 var cid = this.grid.getGridEl().id;
55246         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55247         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55248         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55249         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55250         },
55251         
55252     getColumnRenderers : function(){
55253         var renderers = [];
55254         var cm = this.grid.colModel;
55255         var colCount = cm.getColumnCount();
55256         for(var i = 0; i < colCount; i++){
55257             renderers[i] = cm.getRenderer(i);
55258         }
55259         return renderers;
55260     },
55261     
55262     getColumnIds : function(){
55263         var ids = [];
55264         var cm = this.grid.colModel;
55265         var colCount = cm.getColumnCount();
55266         for(var i = 0; i < colCount; i++){
55267             ids[i] = cm.getColumnId(i);
55268         }
55269         return ids;
55270     },
55271     
55272     getDataIndexes : function(){
55273         if(!this.indexMap){
55274             this.indexMap = this.buildIndexMap();
55275         }
55276         return this.indexMap.colToData;
55277     },
55278     
55279     getColumnIndexByDataIndex : function(dataIndex){
55280         if(!this.indexMap){
55281             this.indexMap = this.buildIndexMap();
55282         }
55283         return this.indexMap.dataToCol[dataIndex];
55284     },
55285     
55286     /**
55287      * Set a css style for a column dynamically. 
55288      * @param {Number} colIndex The index of the column
55289      * @param {String} name The css property name
55290      * @param {String} value The css value
55291      */
55292     setCSSStyle : function(colIndex, name, value){
55293         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55294         Roo.util.CSS.updateRule(selector, name, value);
55295     },
55296     
55297     generateRules : function(cm){
55298         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55299         Roo.util.CSS.removeStyleSheet(rulesId);
55300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55301             var cid = cm.getColumnId(i);
55302             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55303                          this.tdSelector, cid, " {\n}\n",
55304                          this.hdSelector, cid, " {\n}\n",
55305                          this.splitSelector, cid, " {\n}\n");
55306         }
55307         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55308     }
55309 });/*
55310  * Based on:
55311  * Ext JS Library 1.1.1
55312  * Copyright(c) 2006-2007, Ext JS, LLC.
55313  *
55314  * Originally Released Under LGPL - original licence link has changed is not relivant.
55315  *
55316  * Fork - LGPL
55317  * <script type="text/javascript">
55318  */
55319
55320 // private
55321 // This is a support class used internally by the Grid components
55322 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55323     this.grid = grid;
55324     this.view = grid.getView();
55325     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55326     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55327     if(hd2){
55328         this.setHandleElId(Roo.id(hd));
55329         this.setOuterHandleElId(Roo.id(hd2));
55330     }
55331     this.scroll = false;
55332 };
55333 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55334     maxDragWidth: 120,
55335     getDragData : function(e){
55336         var t = Roo.lib.Event.getTarget(e);
55337         var h = this.view.findHeaderCell(t);
55338         if(h){
55339             return {ddel: h.firstChild, header:h};
55340         }
55341         return false;
55342     },
55343
55344     onInitDrag : function(e){
55345         this.view.headersDisabled = true;
55346         var clone = this.dragData.ddel.cloneNode(true);
55347         clone.id = Roo.id();
55348         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55349         this.proxy.update(clone);
55350         return true;
55351     },
55352
55353     afterValidDrop : function(){
55354         var v = this.view;
55355         setTimeout(function(){
55356             v.headersDisabled = false;
55357         }, 50);
55358     },
55359
55360     afterInvalidDrop : function(){
55361         var v = this.view;
55362         setTimeout(function(){
55363             v.headersDisabled = false;
55364         }, 50);
55365     }
55366 });
55367 /*
55368  * Based on:
55369  * Ext JS Library 1.1.1
55370  * Copyright(c) 2006-2007, Ext JS, LLC.
55371  *
55372  * Originally Released Under LGPL - original licence link has changed is not relivant.
55373  *
55374  * Fork - LGPL
55375  * <script type="text/javascript">
55376  */
55377 // private
55378 // This is a support class used internally by the Grid components
55379 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55380     this.grid = grid;
55381     this.view = grid.getView();
55382     // split the proxies so they don't interfere with mouse events
55383     this.proxyTop = Roo.DomHelper.append(document.body, {
55384         cls:"col-move-top", html:"&#160;"
55385     }, true);
55386     this.proxyBottom = Roo.DomHelper.append(document.body, {
55387         cls:"col-move-bottom", html:"&#160;"
55388     }, true);
55389     this.proxyTop.hide = this.proxyBottom.hide = function(){
55390         this.setLeftTop(-100,-100);
55391         this.setStyle("visibility", "hidden");
55392     };
55393     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55394     // temporarily disabled
55395     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55396     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55397 };
55398 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55399     proxyOffsets : [-4, -9],
55400     fly: Roo.Element.fly,
55401
55402     getTargetFromEvent : function(e){
55403         var t = Roo.lib.Event.getTarget(e);
55404         var cindex = this.view.findCellIndex(t);
55405         if(cindex !== false){
55406             return this.view.getHeaderCell(cindex);
55407         }
55408         return null;
55409     },
55410
55411     nextVisible : function(h){
55412         var v = this.view, cm = this.grid.colModel;
55413         h = h.nextSibling;
55414         while(h){
55415             if(!cm.isHidden(v.getCellIndex(h))){
55416                 return h;
55417             }
55418             h = h.nextSibling;
55419         }
55420         return null;
55421     },
55422
55423     prevVisible : function(h){
55424         var v = this.view, cm = this.grid.colModel;
55425         h = h.prevSibling;
55426         while(h){
55427             if(!cm.isHidden(v.getCellIndex(h))){
55428                 return h;
55429             }
55430             h = h.prevSibling;
55431         }
55432         return null;
55433     },
55434
55435     positionIndicator : function(h, n, e){
55436         var x = Roo.lib.Event.getPageX(e);
55437         var r = Roo.lib.Dom.getRegion(n.firstChild);
55438         var px, pt, py = r.top + this.proxyOffsets[1];
55439         if((r.right - x) <= (r.right-r.left)/2){
55440             px = r.right+this.view.borderWidth;
55441             pt = "after";
55442         }else{
55443             px = r.left;
55444             pt = "before";
55445         }
55446         var oldIndex = this.view.getCellIndex(h);
55447         var newIndex = this.view.getCellIndex(n);
55448
55449         if(this.grid.colModel.isFixed(newIndex)){
55450             return false;
55451         }
55452
55453         var locked = this.grid.colModel.isLocked(newIndex);
55454
55455         if(pt == "after"){
55456             newIndex++;
55457         }
55458         if(oldIndex < newIndex){
55459             newIndex--;
55460         }
55461         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55462             return false;
55463         }
55464         px +=  this.proxyOffsets[0];
55465         this.proxyTop.setLeftTop(px, py);
55466         this.proxyTop.show();
55467         if(!this.bottomOffset){
55468             this.bottomOffset = this.view.mainHd.getHeight();
55469         }
55470         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55471         this.proxyBottom.show();
55472         return pt;
55473     },
55474
55475     onNodeEnter : function(n, dd, e, data){
55476         if(data.header != n){
55477             this.positionIndicator(data.header, n, e);
55478         }
55479     },
55480
55481     onNodeOver : function(n, dd, e, data){
55482         var result = false;
55483         if(data.header != n){
55484             result = this.positionIndicator(data.header, n, e);
55485         }
55486         if(!result){
55487             this.proxyTop.hide();
55488             this.proxyBottom.hide();
55489         }
55490         return result ? this.dropAllowed : this.dropNotAllowed;
55491     },
55492
55493     onNodeOut : function(n, dd, e, data){
55494         this.proxyTop.hide();
55495         this.proxyBottom.hide();
55496     },
55497
55498     onNodeDrop : function(n, dd, e, data){
55499         var h = data.header;
55500         if(h != n){
55501             var cm = this.grid.colModel;
55502             var x = Roo.lib.Event.getPageX(e);
55503             var r = Roo.lib.Dom.getRegion(n.firstChild);
55504             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55505             var oldIndex = this.view.getCellIndex(h);
55506             var newIndex = this.view.getCellIndex(n);
55507             var locked = cm.isLocked(newIndex);
55508             if(pt == "after"){
55509                 newIndex++;
55510             }
55511             if(oldIndex < newIndex){
55512                 newIndex--;
55513             }
55514             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55515                 return false;
55516             }
55517             cm.setLocked(oldIndex, locked, true);
55518             cm.moveColumn(oldIndex, newIndex);
55519             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55520             return true;
55521         }
55522         return false;
55523     }
55524 });
55525 /*
55526  * Based on:
55527  * Ext JS Library 1.1.1
55528  * Copyright(c) 2006-2007, Ext JS, LLC.
55529  *
55530  * Originally Released Under LGPL - original licence link has changed is not relivant.
55531  *
55532  * Fork - LGPL
55533  * <script type="text/javascript">
55534  */
55535   
55536 /**
55537  * @class Roo.grid.GridView
55538  * @extends Roo.util.Observable
55539  *
55540  * @constructor
55541  * @param {Object} config
55542  */
55543 Roo.grid.GridView = function(config){
55544     Roo.grid.GridView.superclass.constructor.call(this);
55545     this.el = null;
55546
55547     Roo.apply(this, config);
55548 };
55549
55550 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55551
55552     unselectable :  'unselectable="on"',
55553     unselectableCls :  'x-unselectable',
55554     
55555     
55556     rowClass : "x-grid-row",
55557
55558     cellClass : "x-grid-col",
55559
55560     tdClass : "x-grid-td",
55561
55562     hdClass : "x-grid-hd",
55563
55564     splitClass : "x-grid-split",
55565
55566     sortClasses : ["sort-asc", "sort-desc"],
55567
55568     enableMoveAnim : false,
55569
55570     hlColor: "C3DAF9",
55571
55572     dh : Roo.DomHelper,
55573
55574     fly : Roo.Element.fly,
55575
55576     css : Roo.util.CSS,
55577
55578     borderWidth: 1,
55579
55580     splitOffset: 3,
55581
55582     scrollIncrement : 22,
55583
55584     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55585
55586     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55587
55588     bind : function(ds, cm){
55589         if(this.ds){
55590             this.ds.un("load", this.onLoad, this);
55591             this.ds.un("datachanged", this.onDataChange, this);
55592             this.ds.un("add", this.onAdd, this);
55593             this.ds.un("remove", this.onRemove, this);
55594             this.ds.un("update", this.onUpdate, this);
55595             this.ds.un("clear", this.onClear, this);
55596         }
55597         if(ds){
55598             ds.on("load", this.onLoad, this);
55599             ds.on("datachanged", this.onDataChange, this);
55600             ds.on("add", this.onAdd, this);
55601             ds.on("remove", this.onRemove, this);
55602             ds.on("update", this.onUpdate, this);
55603             ds.on("clear", this.onClear, this);
55604         }
55605         this.ds = ds;
55606
55607         if(this.cm){
55608             this.cm.un("widthchange", this.onColWidthChange, this);
55609             this.cm.un("headerchange", this.onHeaderChange, this);
55610             this.cm.un("hiddenchange", this.onHiddenChange, this);
55611             this.cm.un("columnmoved", this.onColumnMove, this);
55612             this.cm.un("columnlockchange", this.onColumnLock, this);
55613         }
55614         if(cm){
55615             this.generateRules(cm);
55616             cm.on("widthchange", this.onColWidthChange, this);
55617             cm.on("headerchange", this.onHeaderChange, this);
55618             cm.on("hiddenchange", this.onHiddenChange, this);
55619             cm.on("columnmoved", this.onColumnMove, this);
55620             cm.on("columnlockchange", this.onColumnLock, this);
55621         }
55622         this.cm = cm;
55623     },
55624
55625     init: function(grid){
55626         Roo.grid.GridView.superclass.init.call(this, grid);
55627
55628         this.bind(grid.dataSource, grid.colModel);
55629
55630         grid.on("headerclick", this.handleHeaderClick, this);
55631
55632         if(grid.trackMouseOver){
55633             grid.on("mouseover", this.onRowOver, this);
55634             grid.on("mouseout", this.onRowOut, this);
55635         }
55636         grid.cancelTextSelection = function(){};
55637         this.gridId = grid.id;
55638
55639         var tpls = this.templates || {};
55640
55641         if(!tpls.master){
55642             tpls.master = new Roo.Template(
55643                '<div class="x-grid" hidefocus="true">',
55644                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55645                   '<div class="x-grid-topbar"></div>',
55646                   '<div class="x-grid-scroller"><div></div></div>',
55647                   '<div class="x-grid-locked">',
55648                       '<div class="x-grid-header">{lockedHeader}</div>',
55649                       '<div class="x-grid-body">{lockedBody}</div>',
55650                   "</div>",
55651                   '<div class="x-grid-viewport">',
55652                       '<div class="x-grid-header">{header}</div>',
55653                       '<div class="x-grid-body">{body}</div>',
55654                   "</div>",
55655                   '<div class="x-grid-bottombar"></div>',
55656                  
55657                   '<div class="x-grid-resize-proxy">&#160;</div>',
55658                "</div>"
55659             );
55660             tpls.master.disableformats = true;
55661         }
55662
55663         if(!tpls.header){
55664             tpls.header = new Roo.Template(
55665                '<table border="0" cellspacing="0" cellpadding="0">',
55666                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55667                "</table>{splits}"
55668             );
55669             tpls.header.disableformats = true;
55670         }
55671         tpls.header.compile();
55672
55673         if(!tpls.hcell){
55674             tpls.hcell = new Roo.Template(
55675                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55676                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55677                 "</div></td>"
55678              );
55679              tpls.hcell.disableFormats = true;
55680         }
55681         tpls.hcell.compile();
55682
55683         if(!tpls.hsplit){
55684             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55685                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55686             tpls.hsplit.disableFormats = true;
55687         }
55688         tpls.hsplit.compile();
55689
55690         if(!tpls.body){
55691             tpls.body = new Roo.Template(
55692                '<table border="0" cellspacing="0" cellpadding="0">',
55693                "<tbody>{rows}</tbody>",
55694                "</table>"
55695             );
55696             tpls.body.disableFormats = true;
55697         }
55698         tpls.body.compile();
55699
55700         if(!tpls.row){
55701             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55702             tpls.row.disableFormats = true;
55703         }
55704         tpls.row.compile();
55705
55706         if(!tpls.cell){
55707             tpls.cell = new Roo.Template(
55708                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55709                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55710                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55711                 "</td>"
55712             );
55713             tpls.cell.disableFormats = true;
55714         }
55715         tpls.cell.compile();
55716
55717         this.templates = tpls;
55718     },
55719
55720     // remap these for backwards compat
55721     onColWidthChange : function(){
55722         this.updateColumns.apply(this, arguments);
55723     },
55724     onHeaderChange : function(){
55725         this.updateHeaders.apply(this, arguments);
55726     }, 
55727     onHiddenChange : function(){
55728         this.handleHiddenChange.apply(this, arguments);
55729     },
55730     onColumnMove : function(){
55731         this.handleColumnMove.apply(this, arguments);
55732     },
55733     onColumnLock : function(){
55734         this.handleLockChange.apply(this, arguments);
55735     },
55736
55737     onDataChange : function(){
55738         this.refresh();
55739         this.updateHeaderSortState();
55740     },
55741
55742     onClear : function(){
55743         this.refresh();
55744     },
55745
55746     onUpdate : function(ds, record){
55747         this.refreshRow(record);
55748     },
55749
55750     refreshRow : function(record){
55751         var ds = this.ds, index;
55752         if(typeof record == 'number'){
55753             index = record;
55754             record = ds.getAt(index);
55755         }else{
55756             index = ds.indexOf(record);
55757         }
55758         this.insertRows(ds, index, index, true);
55759         this.onRemove(ds, record, index+1, true);
55760         this.syncRowHeights(index, index);
55761         this.layout();
55762         this.fireEvent("rowupdated", this, index, record);
55763     },
55764
55765     onAdd : function(ds, records, index){
55766         this.insertRows(ds, index, index + (records.length-1));
55767     },
55768
55769     onRemove : function(ds, record, index, isUpdate){
55770         if(isUpdate !== true){
55771             this.fireEvent("beforerowremoved", this, index, record);
55772         }
55773         var bt = this.getBodyTable(), lt = this.getLockedTable();
55774         if(bt.rows[index]){
55775             bt.firstChild.removeChild(bt.rows[index]);
55776         }
55777         if(lt.rows[index]){
55778             lt.firstChild.removeChild(lt.rows[index]);
55779         }
55780         if(isUpdate !== true){
55781             this.stripeRows(index);
55782             this.syncRowHeights(index, index);
55783             this.layout();
55784             this.fireEvent("rowremoved", this, index, record);
55785         }
55786     },
55787
55788     onLoad : function(){
55789         this.scrollToTop();
55790     },
55791
55792     /**
55793      * Scrolls the grid to the top
55794      */
55795     scrollToTop : function(){
55796         if(this.scroller){
55797             this.scroller.dom.scrollTop = 0;
55798             this.syncScroll();
55799         }
55800     },
55801
55802     /**
55803      * Gets a panel in the header of the grid that can be used for toolbars etc.
55804      * After modifying the contents of this panel a call to grid.autoSize() may be
55805      * required to register any changes in size.
55806      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55807      * @return Roo.Element
55808      */
55809     getHeaderPanel : function(doShow){
55810         if(doShow){
55811             this.headerPanel.show();
55812         }
55813         return this.headerPanel;
55814     },
55815
55816     /**
55817      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55818      * After modifying the contents of this panel a call to grid.autoSize() may be
55819      * required to register any changes in size.
55820      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55821      * @return Roo.Element
55822      */
55823     getFooterPanel : function(doShow){
55824         if(doShow){
55825             this.footerPanel.show();
55826         }
55827         return this.footerPanel;
55828     },
55829
55830     initElements : function(){
55831         var E = Roo.Element;
55832         var el = this.grid.getGridEl().dom.firstChild;
55833         var cs = el.childNodes;
55834
55835         this.el = new E(el);
55836         
55837          this.focusEl = new E(el.firstChild);
55838         this.focusEl.swallowEvent("click", true);
55839         
55840         this.headerPanel = new E(cs[1]);
55841         this.headerPanel.enableDisplayMode("block");
55842
55843         this.scroller = new E(cs[2]);
55844         this.scrollSizer = new E(this.scroller.dom.firstChild);
55845
55846         this.lockedWrap = new E(cs[3]);
55847         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55848         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55849
55850         this.mainWrap = new E(cs[4]);
55851         this.mainHd = new E(this.mainWrap.dom.firstChild);
55852         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55853
55854         this.footerPanel = new E(cs[5]);
55855         this.footerPanel.enableDisplayMode("block");
55856
55857         this.resizeProxy = new E(cs[6]);
55858
55859         this.headerSelector = String.format(
55860            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55861            this.lockedHd.id, this.mainHd.id
55862         );
55863
55864         this.splitterSelector = String.format(
55865            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55866            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55867         );
55868     },
55869     idToCssName : function(s)
55870     {
55871         return s.replace(/[^a-z0-9]+/ig, '-');
55872     },
55873
55874     getHeaderCell : function(index){
55875         return Roo.DomQuery.select(this.headerSelector)[index];
55876     },
55877
55878     getHeaderCellMeasure : function(index){
55879         return this.getHeaderCell(index).firstChild;
55880     },
55881
55882     getHeaderCellText : function(index){
55883         return this.getHeaderCell(index).firstChild.firstChild;
55884     },
55885
55886     getLockedTable : function(){
55887         return this.lockedBody.dom.firstChild;
55888     },
55889
55890     getBodyTable : function(){
55891         return this.mainBody.dom.firstChild;
55892     },
55893
55894     getLockedRow : function(index){
55895         return this.getLockedTable().rows[index];
55896     },
55897
55898     getRow : function(index){
55899         return this.getBodyTable().rows[index];
55900     },
55901
55902     getRowComposite : function(index){
55903         if(!this.rowEl){
55904             this.rowEl = new Roo.CompositeElementLite();
55905         }
55906         var els = [], lrow, mrow;
55907         if(lrow = this.getLockedRow(index)){
55908             els.push(lrow);
55909         }
55910         if(mrow = this.getRow(index)){
55911             els.push(mrow);
55912         }
55913         this.rowEl.elements = els;
55914         return this.rowEl;
55915     },
55916     /**
55917      * Gets the 'td' of the cell
55918      * 
55919      * @param {Integer} rowIndex row to select
55920      * @param {Integer} colIndex column to select
55921      * 
55922      * @return {Object} 
55923      */
55924     getCell : function(rowIndex, colIndex){
55925         var locked = this.cm.getLockedCount();
55926         var source;
55927         if(colIndex < locked){
55928             source = this.lockedBody.dom.firstChild;
55929         }else{
55930             source = this.mainBody.dom.firstChild;
55931             colIndex -= locked;
55932         }
55933         return source.rows[rowIndex].childNodes[colIndex];
55934     },
55935
55936     getCellText : function(rowIndex, colIndex){
55937         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55938     },
55939
55940     getCellBox : function(cell){
55941         var b = this.fly(cell).getBox();
55942         if(Roo.isOpera){ // opera fails to report the Y
55943             b.y = cell.offsetTop + this.mainBody.getY();
55944         }
55945         return b;
55946     },
55947
55948     getCellIndex : function(cell){
55949         var id = String(cell.className).match(this.cellRE);
55950         if(id){
55951             return parseInt(id[1], 10);
55952         }
55953         return 0;
55954     },
55955
55956     findHeaderIndex : function(n){
55957         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55958         return r ? this.getCellIndex(r) : false;
55959     },
55960
55961     findHeaderCell : function(n){
55962         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55963         return r ? r : false;
55964     },
55965
55966     findRowIndex : function(n){
55967         if(!n){
55968             return false;
55969         }
55970         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55971         return r ? r.rowIndex : false;
55972     },
55973
55974     findCellIndex : function(node){
55975         var stop = this.el.dom;
55976         while(node && node != stop){
55977             if(this.findRE.test(node.className)){
55978                 return this.getCellIndex(node);
55979             }
55980             node = node.parentNode;
55981         }
55982         return false;
55983     },
55984
55985     getColumnId : function(index){
55986         return this.cm.getColumnId(index);
55987     },
55988
55989     getSplitters : function()
55990     {
55991         if(this.splitterSelector){
55992            return Roo.DomQuery.select(this.splitterSelector);
55993         }else{
55994             return null;
55995       }
55996     },
55997
55998     getSplitter : function(index){
55999         return this.getSplitters()[index];
56000     },
56001
56002     onRowOver : function(e, t){
56003         var row;
56004         if((row = this.findRowIndex(t)) !== false){
56005             this.getRowComposite(row).addClass("x-grid-row-over");
56006         }
56007     },
56008
56009     onRowOut : function(e, t){
56010         var row;
56011         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56012             this.getRowComposite(row).removeClass("x-grid-row-over");
56013         }
56014     },
56015
56016     renderHeaders : function(){
56017         var cm = this.cm;
56018         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56019         var cb = [], lb = [], sb = [], lsb = [], p = {};
56020         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56021             p.cellId = "x-grid-hd-0-" + i;
56022             p.splitId = "x-grid-csplit-0-" + i;
56023             p.id = cm.getColumnId(i);
56024             p.value = cm.getColumnHeader(i) || "";
56025             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56026             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56027             if(!cm.isLocked(i)){
56028                 cb[cb.length] = ct.apply(p);
56029                 sb[sb.length] = st.apply(p);
56030             }else{
56031                 lb[lb.length] = ct.apply(p);
56032                 lsb[lsb.length] = st.apply(p);
56033             }
56034         }
56035         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56036                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56037     },
56038
56039     updateHeaders : function(){
56040         var html = this.renderHeaders();
56041         this.lockedHd.update(html[0]);
56042         this.mainHd.update(html[1]);
56043     },
56044
56045     /**
56046      * Focuses the specified row.
56047      * @param {Number} row The row index
56048      */
56049     focusRow : function(row)
56050     {
56051         //Roo.log('GridView.focusRow');
56052         var x = this.scroller.dom.scrollLeft;
56053         this.focusCell(row, 0, false);
56054         this.scroller.dom.scrollLeft = x;
56055     },
56056
56057     /**
56058      * Focuses the specified cell.
56059      * @param {Number} row The row index
56060      * @param {Number} col The column index
56061      * @param {Boolean} hscroll false to disable horizontal scrolling
56062      */
56063     focusCell : function(row, col, hscroll)
56064     {
56065         //Roo.log('GridView.focusCell');
56066         var el = this.ensureVisible(row, col, hscroll);
56067         this.focusEl.alignTo(el, "tl-tl");
56068         if(Roo.isGecko){
56069             this.focusEl.focus();
56070         }else{
56071             this.focusEl.focus.defer(1, this.focusEl);
56072         }
56073     },
56074
56075     /**
56076      * Scrolls the specified cell into view
56077      * @param {Number} row The row index
56078      * @param {Number} col The column index
56079      * @param {Boolean} hscroll false to disable horizontal scrolling
56080      */
56081     ensureVisible : function(row, col, hscroll)
56082     {
56083         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56084         //return null; //disable for testing.
56085         if(typeof row != "number"){
56086             row = row.rowIndex;
56087         }
56088         if(row < 0 && row >= this.ds.getCount()){
56089             return  null;
56090         }
56091         col = (col !== undefined ? col : 0);
56092         var cm = this.grid.colModel;
56093         while(cm.isHidden(col)){
56094             col++;
56095         }
56096
56097         var el = this.getCell(row, col);
56098         if(!el){
56099             return null;
56100         }
56101         var c = this.scroller.dom;
56102
56103         var ctop = parseInt(el.offsetTop, 10);
56104         var cleft = parseInt(el.offsetLeft, 10);
56105         var cbot = ctop + el.offsetHeight;
56106         var cright = cleft + el.offsetWidth;
56107         
56108         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56109         var stop = parseInt(c.scrollTop, 10);
56110         var sleft = parseInt(c.scrollLeft, 10);
56111         var sbot = stop + ch;
56112         var sright = sleft + c.clientWidth;
56113         /*
56114         Roo.log('GridView.ensureVisible:' +
56115                 ' ctop:' + ctop +
56116                 ' c.clientHeight:' + c.clientHeight +
56117                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56118                 ' stop:' + stop +
56119                 ' cbot:' + cbot +
56120                 ' sbot:' + sbot +
56121                 ' ch:' + ch  
56122                 );
56123         */
56124         if(ctop < stop){
56125              c.scrollTop = ctop;
56126             //Roo.log("set scrolltop to ctop DISABLE?");
56127         }else if(cbot > sbot){
56128             //Roo.log("set scrolltop to cbot-ch");
56129             c.scrollTop = cbot-ch;
56130         }
56131         
56132         if(hscroll !== false){
56133             if(cleft < sleft){
56134                 c.scrollLeft = cleft;
56135             }else if(cright > sright){
56136                 c.scrollLeft = cright-c.clientWidth;
56137             }
56138         }
56139          
56140         return el;
56141     },
56142
56143     updateColumns : function(){
56144         this.grid.stopEditing();
56145         var cm = this.grid.colModel, colIds = this.getColumnIds();
56146         //var totalWidth = cm.getTotalWidth();
56147         var pos = 0;
56148         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56149             //if(cm.isHidden(i)) continue;
56150             var w = cm.getColumnWidth(i);
56151             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56152             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56153         }
56154         this.updateSplitters();
56155     },
56156
56157     generateRules : function(cm){
56158         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56159         Roo.util.CSS.removeStyleSheet(rulesId);
56160         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56161             var cid = cm.getColumnId(i);
56162             var align = '';
56163             if(cm.config[i].align){
56164                 align = 'text-align:'+cm.config[i].align+';';
56165             }
56166             var hidden = '';
56167             if(cm.isHidden(i)){
56168                 hidden = 'display:none;';
56169             }
56170             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56171             ruleBuf.push(
56172                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56173                     this.hdSelector, cid, " {\n", align, width, "}\n",
56174                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56175                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56176         }
56177         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56178     },
56179
56180     updateSplitters : function(){
56181         var cm = this.cm, s = this.getSplitters();
56182         if(s){ // splitters not created yet
56183             var pos = 0, locked = true;
56184             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56185                 if(cm.isHidden(i)) {
56186                     continue;
56187                 }
56188                 var w = cm.getColumnWidth(i); // make sure it's a number
56189                 if(!cm.isLocked(i) && locked){
56190                     pos = 0;
56191                     locked = false;
56192                 }
56193                 pos += w;
56194                 s[i].style.left = (pos-this.splitOffset) + "px";
56195             }
56196         }
56197     },
56198
56199     handleHiddenChange : function(colModel, colIndex, hidden){
56200         if(hidden){
56201             this.hideColumn(colIndex);
56202         }else{
56203             this.unhideColumn(colIndex);
56204         }
56205     },
56206
56207     hideColumn : function(colIndex){
56208         var cid = this.getColumnId(colIndex);
56209         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56210         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56211         if(Roo.isSafari){
56212             this.updateHeaders();
56213         }
56214         this.updateSplitters();
56215         this.layout();
56216     },
56217
56218     unhideColumn : function(colIndex){
56219         var cid = this.getColumnId(colIndex);
56220         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56221         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56222
56223         if(Roo.isSafari){
56224             this.updateHeaders();
56225         }
56226         this.updateSplitters();
56227         this.layout();
56228     },
56229
56230     insertRows : function(dm, firstRow, lastRow, isUpdate){
56231         if(firstRow == 0 && lastRow == dm.getCount()-1){
56232             this.refresh();
56233         }else{
56234             if(!isUpdate){
56235                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56236             }
56237             var s = this.getScrollState();
56238             var markup = this.renderRows(firstRow, lastRow);
56239             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56240             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56241             this.restoreScroll(s);
56242             if(!isUpdate){
56243                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56244                 this.syncRowHeights(firstRow, lastRow);
56245                 this.stripeRows(firstRow);
56246                 this.layout();
56247             }
56248         }
56249     },
56250
56251     bufferRows : function(markup, target, index){
56252         var before = null, trows = target.rows, tbody = target.tBodies[0];
56253         if(index < trows.length){
56254             before = trows[index];
56255         }
56256         var b = document.createElement("div");
56257         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56258         var rows = b.firstChild.rows;
56259         for(var i = 0, len = rows.length; i < len; i++){
56260             if(before){
56261                 tbody.insertBefore(rows[0], before);
56262             }else{
56263                 tbody.appendChild(rows[0]);
56264             }
56265         }
56266         b.innerHTML = "";
56267         b = null;
56268     },
56269
56270     deleteRows : function(dm, firstRow, lastRow){
56271         if(dm.getRowCount()<1){
56272             this.fireEvent("beforerefresh", this);
56273             this.mainBody.update("");
56274             this.lockedBody.update("");
56275             this.fireEvent("refresh", this);
56276         }else{
56277             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56278             var bt = this.getBodyTable();
56279             var tbody = bt.firstChild;
56280             var rows = bt.rows;
56281             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56282                 tbody.removeChild(rows[firstRow]);
56283             }
56284             this.stripeRows(firstRow);
56285             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56286         }
56287     },
56288
56289     updateRows : function(dataSource, firstRow, lastRow){
56290         var s = this.getScrollState();
56291         this.refresh();
56292         this.restoreScroll(s);
56293     },
56294
56295     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56296         if(!noRefresh){
56297            this.refresh();
56298         }
56299         this.updateHeaderSortState();
56300     },
56301
56302     getScrollState : function(){
56303         
56304         var sb = this.scroller.dom;
56305         return {left: sb.scrollLeft, top: sb.scrollTop};
56306     },
56307
56308     stripeRows : function(startRow){
56309         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56310             return;
56311         }
56312         startRow = startRow || 0;
56313         var rows = this.getBodyTable().rows;
56314         var lrows = this.getLockedTable().rows;
56315         var cls = ' x-grid-row-alt ';
56316         for(var i = startRow, len = rows.length; i < len; i++){
56317             var row = rows[i], lrow = lrows[i];
56318             var isAlt = ((i+1) % 2 == 0);
56319             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56320             if(isAlt == hasAlt){
56321                 continue;
56322             }
56323             if(isAlt){
56324                 row.className += " x-grid-row-alt";
56325             }else{
56326                 row.className = row.className.replace("x-grid-row-alt", "");
56327             }
56328             if(lrow){
56329                 lrow.className = row.className;
56330             }
56331         }
56332     },
56333
56334     restoreScroll : function(state){
56335         //Roo.log('GridView.restoreScroll');
56336         var sb = this.scroller.dom;
56337         sb.scrollLeft = state.left;
56338         sb.scrollTop = state.top;
56339         this.syncScroll();
56340     },
56341
56342     syncScroll : function(){
56343         //Roo.log('GridView.syncScroll');
56344         var sb = this.scroller.dom;
56345         var sh = this.mainHd.dom;
56346         var bs = this.mainBody.dom;
56347         var lv = this.lockedBody.dom;
56348         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56349         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56350     },
56351
56352     handleScroll : function(e){
56353         this.syncScroll();
56354         var sb = this.scroller.dom;
56355         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56356         e.stopEvent();
56357     },
56358
56359     handleWheel : function(e){
56360         var d = e.getWheelDelta();
56361         this.scroller.dom.scrollTop -= d*22;
56362         // set this here to prevent jumpy scrolling on large tables
56363         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56364         e.stopEvent();
56365     },
56366
56367     renderRows : function(startRow, endRow){
56368         // pull in all the crap needed to render rows
56369         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56370         var colCount = cm.getColumnCount();
56371
56372         if(ds.getCount() < 1){
56373             return ["", ""];
56374         }
56375
56376         // build a map for all the columns
56377         var cs = [];
56378         for(var i = 0; i < colCount; i++){
56379             var name = cm.getDataIndex(i);
56380             cs[i] = {
56381                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56382                 renderer : cm.getRenderer(i),
56383                 id : cm.getColumnId(i),
56384                 locked : cm.isLocked(i),
56385                 has_editor : cm.isCellEditable(i)
56386             };
56387         }
56388
56389         startRow = startRow || 0;
56390         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56391
56392         // records to render
56393         var rs = ds.getRange(startRow, endRow);
56394
56395         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56396     },
56397
56398     // As much as I hate to duplicate code, this was branched because FireFox really hates
56399     // [].join("") on strings. The performance difference was substantial enough to
56400     // branch this function
56401     doRender : Roo.isGecko ?
56402             function(cs, rs, ds, startRow, colCount, stripe){
56403                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56404                 // buffers
56405                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56406                 
56407                 var hasListener = this.grid.hasListener('rowclass');
56408                 var rowcfg = {};
56409                 for(var j = 0, len = rs.length; j < len; j++){
56410                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56411                     for(var i = 0; i < colCount; i++){
56412                         c = cs[i];
56413                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56414                         p.id = c.id;
56415                         p.css = p.attr = "";
56416                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56417                         if(p.value == undefined || p.value === "") {
56418                             p.value = "&#160;";
56419                         }
56420                         if(c.has_editor){
56421                             p.css += ' x-grid-editable-cell';
56422                         }
56423                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56424                             p.css +=  ' x-grid-dirty-cell';
56425                         }
56426                         var markup = ct.apply(p);
56427                         if(!c.locked){
56428                             cb+= markup;
56429                         }else{
56430                             lcb+= markup;
56431                         }
56432                     }
56433                     var alt = [];
56434                     if(stripe && ((rowIndex+1) % 2 == 0)){
56435                         alt.push("x-grid-row-alt")
56436                     }
56437                     if(r.dirty){
56438                         alt.push(  " x-grid-dirty-row");
56439                     }
56440                     rp.cells = lcb;
56441                     if(this.getRowClass){
56442                         alt.push(this.getRowClass(r, rowIndex));
56443                     }
56444                     if (hasListener) {
56445                         rowcfg = {
56446                              
56447                             record: r,
56448                             rowIndex : rowIndex,
56449                             rowClass : ''
56450                         };
56451                         this.grid.fireEvent('rowclass', this, rowcfg);
56452                         alt.push(rowcfg.rowClass);
56453                     }
56454                     rp.alt = alt.join(" ");
56455                     lbuf+= rt.apply(rp);
56456                     rp.cells = cb;
56457                     buf+=  rt.apply(rp);
56458                 }
56459                 return [lbuf, buf];
56460             } :
56461             function(cs, rs, ds, startRow, colCount, stripe){
56462                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56463                 // buffers
56464                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56465                 var hasListener = this.grid.hasListener('rowclass');
56466  
56467                 var rowcfg = {};
56468                 for(var j = 0, len = rs.length; j < len; j++){
56469                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56470                     for(var i = 0; i < colCount; i++){
56471                         c = cs[i];
56472                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56473                         p.id = c.id;
56474                         p.css = p.attr = "";
56475                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56476                         if(p.value == undefined || p.value === "") {
56477                             p.value = "&#160;";
56478                         }
56479                         //Roo.log(c);
56480                          if(c.has_editor){
56481                             p.css += ' x-grid-editable-cell';
56482                         }
56483                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56484                             p.css += ' x-grid-dirty-cell' 
56485                         }
56486                         
56487                         var markup = ct.apply(p);
56488                         if(!c.locked){
56489                             cb[cb.length] = markup;
56490                         }else{
56491                             lcb[lcb.length] = markup;
56492                         }
56493                     }
56494                     var alt = [];
56495                     if(stripe && ((rowIndex+1) % 2 == 0)){
56496                         alt.push( "x-grid-row-alt");
56497                     }
56498                     if(r.dirty){
56499                         alt.push(" x-grid-dirty-row");
56500                     }
56501                     rp.cells = lcb;
56502                     if(this.getRowClass){
56503                         alt.push( this.getRowClass(r, rowIndex));
56504                     }
56505                     if (hasListener) {
56506                         rowcfg = {
56507                              
56508                             record: r,
56509                             rowIndex : rowIndex,
56510                             rowClass : ''
56511                         };
56512                         this.grid.fireEvent('rowclass', this, rowcfg);
56513                         alt.push(rowcfg.rowClass);
56514                     }
56515                     
56516                     rp.alt = alt.join(" ");
56517                     rp.cells = lcb.join("");
56518                     lbuf[lbuf.length] = rt.apply(rp);
56519                     rp.cells = cb.join("");
56520                     buf[buf.length] =  rt.apply(rp);
56521                 }
56522                 return [lbuf.join(""), buf.join("")];
56523             },
56524
56525     renderBody : function(){
56526         var markup = this.renderRows();
56527         var bt = this.templates.body;
56528         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56529     },
56530
56531     /**
56532      * Refreshes the grid
56533      * @param {Boolean} headersToo
56534      */
56535     refresh : function(headersToo){
56536         this.fireEvent("beforerefresh", this);
56537         this.grid.stopEditing();
56538         var result = this.renderBody();
56539         this.lockedBody.update(result[0]);
56540         this.mainBody.update(result[1]);
56541         if(headersToo === true){
56542             this.updateHeaders();
56543             this.updateColumns();
56544             this.updateSplitters();
56545             this.updateHeaderSortState();
56546         }
56547         this.syncRowHeights();
56548         this.layout();
56549         this.fireEvent("refresh", this);
56550     },
56551
56552     handleColumnMove : function(cm, oldIndex, newIndex){
56553         this.indexMap = null;
56554         var s = this.getScrollState();
56555         this.refresh(true);
56556         this.restoreScroll(s);
56557         this.afterMove(newIndex);
56558     },
56559
56560     afterMove : function(colIndex){
56561         if(this.enableMoveAnim && Roo.enableFx){
56562             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56563         }
56564         // if multisort - fix sortOrder, and reload..
56565         if (this.grid.dataSource.multiSort) {
56566             // the we can call sort again..
56567             var dm = this.grid.dataSource;
56568             var cm = this.grid.colModel;
56569             var so = [];
56570             for(var i = 0; i < cm.config.length; i++ ) {
56571                 
56572                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56573                     continue; // dont' bother, it's not in sort list or being set.
56574                 }
56575                 
56576                 so.push(cm.config[i].dataIndex);
56577             };
56578             dm.sortOrder = so;
56579             dm.load(dm.lastOptions);
56580             
56581             
56582         }
56583         
56584     },
56585
56586     updateCell : function(dm, rowIndex, dataIndex){
56587         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56588         if(typeof colIndex == "undefined"){ // not present in grid
56589             return;
56590         }
56591         var cm = this.grid.colModel;
56592         var cell = this.getCell(rowIndex, colIndex);
56593         var cellText = this.getCellText(rowIndex, colIndex);
56594
56595         var p = {
56596             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56597             id : cm.getColumnId(colIndex),
56598             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56599         };
56600         var renderer = cm.getRenderer(colIndex);
56601         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56602         if(typeof val == "undefined" || val === "") {
56603             val = "&#160;";
56604         }
56605         cellText.innerHTML = val;
56606         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56607         this.syncRowHeights(rowIndex, rowIndex);
56608     },
56609
56610     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56611         var maxWidth = 0;
56612         if(this.grid.autoSizeHeaders){
56613             var h = this.getHeaderCellMeasure(colIndex);
56614             maxWidth = Math.max(maxWidth, h.scrollWidth);
56615         }
56616         var tb, index;
56617         if(this.cm.isLocked(colIndex)){
56618             tb = this.getLockedTable();
56619             index = colIndex;
56620         }else{
56621             tb = this.getBodyTable();
56622             index = colIndex - this.cm.getLockedCount();
56623         }
56624         if(tb && tb.rows){
56625             var rows = tb.rows;
56626             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56627             for(var i = 0; i < stopIndex; i++){
56628                 var cell = rows[i].childNodes[index].firstChild;
56629                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56630             }
56631         }
56632         return maxWidth + /*margin for error in IE*/ 5;
56633     },
56634     /**
56635      * Autofit a column to its content.
56636      * @param {Number} colIndex
56637      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56638      */
56639      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56640          if(this.cm.isHidden(colIndex)){
56641              return; // can't calc a hidden column
56642          }
56643         if(forceMinSize){
56644             var cid = this.cm.getColumnId(colIndex);
56645             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56646            if(this.grid.autoSizeHeaders){
56647                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56648            }
56649         }
56650         var newWidth = this.calcColumnWidth(colIndex);
56651         this.cm.setColumnWidth(colIndex,
56652             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56653         if(!suppressEvent){
56654             this.grid.fireEvent("columnresize", colIndex, newWidth);
56655         }
56656     },
56657
56658     /**
56659      * Autofits all columns to their content and then expands to fit any extra space in the grid
56660      */
56661      autoSizeColumns : function(){
56662         var cm = this.grid.colModel;
56663         var colCount = cm.getColumnCount();
56664         for(var i = 0; i < colCount; i++){
56665             this.autoSizeColumn(i, true, true);
56666         }
56667         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56668             this.fitColumns();
56669         }else{
56670             this.updateColumns();
56671             this.layout();
56672         }
56673     },
56674
56675     /**
56676      * Autofits all columns to the grid's width proportionate with their current size
56677      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56678      */
56679     fitColumns : function(reserveScrollSpace){
56680         var cm = this.grid.colModel;
56681         var colCount = cm.getColumnCount();
56682         var cols = [];
56683         var width = 0;
56684         var i, w;
56685         for (i = 0; i < colCount; i++){
56686             if(!cm.isHidden(i) && !cm.isFixed(i)){
56687                 w = cm.getColumnWidth(i);
56688                 cols.push(i);
56689                 cols.push(w);
56690                 width += w;
56691             }
56692         }
56693         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56694         if(reserveScrollSpace){
56695             avail -= 17;
56696         }
56697         var frac = (avail - cm.getTotalWidth())/width;
56698         while (cols.length){
56699             w = cols.pop();
56700             i = cols.pop();
56701             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56702         }
56703         this.updateColumns();
56704         this.layout();
56705     },
56706
56707     onRowSelect : function(rowIndex){
56708         var row = this.getRowComposite(rowIndex);
56709         row.addClass("x-grid-row-selected");
56710     },
56711
56712     onRowDeselect : function(rowIndex){
56713         var row = this.getRowComposite(rowIndex);
56714         row.removeClass("x-grid-row-selected");
56715     },
56716
56717     onCellSelect : function(row, col){
56718         var cell = this.getCell(row, col);
56719         if(cell){
56720             Roo.fly(cell).addClass("x-grid-cell-selected");
56721         }
56722     },
56723
56724     onCellDeselect : function(row, col){
56725         var cell = this.getCell(row, col);
56726         if(cell){
56727             Roo.fly(cell).removeClass("x-grid-cell-selected");
56728         }
56729     },
56730
56731     updateHeaderSortState : function(){
56732         
56733         // sort state can be single { field: xxx, direction : yyy}
56734         // or   { xxx=>ASC , yyy : DESC ..... }
56735         
56736         var mstate = {};
56737         if (!this.ds.multiSort) { 
56738             var state = this.ds.getSortState();
56739             if(!state){
56740                 return;
56741             }
56742             mstate[state.field] = state.direction;
56743             // FIXME... - this is not used here.. but might be elsewhere..
56744             this.sortState = state;
56745             
56746         } else {
56747             mstate = this.ds.sortToggle;
56748         }
56749         //remove existing sort classes..
56750         
56751         var sc = this.sortClasses;
56752         var hds = this.el.select(this.headerSelector).removeClass(sc);
56753         
56754         for(var f in mstate) {
56755         
56756             var sortColumn = this.cm.findColumnIndex(f);
56757             
56758             if(sortColumn != -1){
56759                 var sortDir = mstate[f];        
56760                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56761             }
56762         }
56763         
56764          
56765         
56766     },
56767
56768
56769     handleHeaderClick : function(g, index,e){
56770         
56771         Roo.log("header click");
56772         
56773         if (Roo.isTouch) {
56774             // touch events on header are handled by context
56775             this.handleHdCtx(g,index,e);
56776             return;
56777         }
56778         
56779         
56780         if(this.headersDisabled){
56781             return;
56782         }
56783         var dm = g.dataSource, cm = g.colModel;
56784         if(!cm.isSortable(index)){
56785             return;
56786         }
56787         g.stopEditing();
56788         
56789         if (dm.multiSort) {
56790             // update the sortOrder
56791             var so = [];
56792             for(var i = 0; i < cm.config.length; i++ ) {
56793                 
56794                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56795                     continue; // dont' bother, it's not in sort list or being set.
56796                 }
56797                 
56798                 so.push(cm.config[i].dataIndex);
56799             };
56800             dm.sortOrder = so;
56801         }
56802         
56803         
56804         dm.sort(cm.getDataIndex(index));
56805     },
56806
56807
56808     destroy : function(){
56809         if(this.colMenu){
56810             this.colMenu.removeAll();
56811             Roo.menu.MenuMgr.unregister(this.colMenu);
56812             this.colMenu.getEl().remove();
56813             delete this.colMenu;
56814         }
56815         if(this.hmenu){
56816             this.hmenu.removeAll();
56817             Roo.menu.MenuMgr.unregister(this.hmenu);
56818             this.hmenu.getEl().remove();
56819             delete this.hmenu;
56820         }
56821         if(this.grid.enableColumnMove){
56822             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56823             if(dds){
56824                 for(var dd in dds){
56825                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56826                         var elid = dds[dd].dragElId;
56827                         dds[dd].unreg();
56828                         Roo.get(elid).remove();
56829                     } else if(dds[dd].config.isTarget){
56830                         dds[dd].proxyTop.remove();
56831                         dds[dd].proxyBottom.remove();
56832                         dds[dd].unreg();
56833                     }
56834                     if(Roo.dd.DDM.locationCache[dd]){
56835                         delete Roo.dd.DDM.locationCache[dd];
56836                     }
56837                 }
56838                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56839             }
56840         }
56841         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56842         this.bind(null, null);
56843         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56844     },
56845
56846     handleLockChange : function(){
56847         this.refresh(true);
56848     },
56849
56850     onDenyColumnLock : function(){
56851
56852     },
56853
56854     onDenyColumnHide : function(){
56855
56856     },
56857
56858     handleHdMenuClick : function(item){
56859         var index = this.hdCtxIndex;
56860         var cm = this.cm, ds = this.ds;
56861         switch(item.id){
56862             case "asc":
56863                 ds.sort(cm.getDataIndex(index), "ASC");
56864                 break;
56865             case "desc":
56866                 ds.sort(cm.getDataIndex(index), "DESC");
56867                 break;
56868             case "lock":
56869                 var lc = cm.getLockedCount();
56870                 if(cm.getColumnCount(true) <= lc+1){
56871                     this.onDenyColumnLock();
56872                     return;
56873                 }
56874                 if(lc != index){
56875                     cm.setLocked(index, true, true);
56876                     cm.moveColumn(index, lc);
56877                     this.grid.fireEvent("columnmove", index, lc);
56878                 }else{
56879                     cm.setLocked(index, true);
56880                 }
56881             break;
56882             case "unlock":
56883                 var lc = cm.getLockedCount();
56884                 if((lc-1) != index){
56885                     cm.setLocked(index, false, true);
56886                     cm.moveColumn(index, lc-1);
56887                     this.grid.fireEvent("columnmove", index, lc-1);
56888                 }else{
56889                     cm.setLocked(index, false);
56890                 }
56891             break;
56892             case 'wider': // used to expand cols on touch..
56893             case 'narrow':
56894                 var cw = cm.getColumnWidth(index);
56895                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56896                 cw = Math.max(0, cw);
56897                 cw = Math.min(cw,4000);
56898                 cm.setColumnWidth(index, cw);
56899                 break;
56900                 
56901             default:
56902                 index = cm.getIndexById(item.id.substr(4));
56903                 if(index != -1){
56904                     if(item.checked && cm.getColumnCount(true) <= 1){
56905                         this.onDenyColumnHide();
56906                         return false;
56907                     }
56908                     cm.setHidden(index, item.checked);
56909                 }
56910         }
56911         return true;
56912     },
56913
56914     beforeColMenuShow : function(){
56915         var cm = this.cm,  colCount = cm.getColumnCount();
56916         this.colMenu.removeAll();
56917         for(var i = 0; i < colCount; i++){
56918             this.colMenu.add(new Roo.menu.CheckItem({
56919                 id: "col-"+cm.getColumnId(i),
56920                 text: cm.getColumnHeader(i),
56921                 checked: !cm.isHidden(i),
56922                 hideOnClick:false
56923             }));
56924         }
56925     },
56926
56927     handleHdCtx : function(g, index, e){
56928         e.stopEvent();
56929         var hd = this.getHeaderCell(index);
56930         this.hdCtxIndex = index;
56931         var ms = this.hmenu.items, cm = this.cm;
56932         ms.get("asc").setDisabled(!cm.isSortable(index));
56933         ms.get("desc").setDisabled(!cm.isSortable(index));
56934         if(this.grid.enableColLock !== false){
56935             ms.get("lock").setDisabled(cm.isLocked(index));
56936             ms.get("unlock").setDisabled(!cm.isLocked(index));
56937         }
56938         this.hmenu.show(hd, "tl-bl");
56939     },
56940
56941     handleHdOver : function(e){
56942         var hd = this.findHeaderCell(e.getTarget());
56943         if(hd && !this.headersDisabled){
56944             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56945                this.fly(hd).addClass("x-grid-hd-over");
56946             }
56947         }
56948     },
56949
56950     handleHdOut : function(e){
56951         var hd = this.findHeaderCell(e.getTarget());
56952         if(hd){
56953             this.fly(hd).removeClass("x-grid-hd-over");
56954         }
56955     },
56956
56957     handleSplitDblClick : function(e, t){
56958         var i = this.getCellIndex(t);
56959         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56960             this.autoSizeColumn(i, true);
56961             this.layout();
56962         }
56963     },
56964
56965     render : function(){
56966
56967         var cm = this.cm;
56968         var colCount = cm.getColumnCount();
56969
56970         if(this.grid.monitorWindowResize === true){
56971             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56972         }
56973         var header = this.renderHeaders();
56974         var body = this.templates.body.apply({rows:""});
56975         var html = this.templates.master.apply({
56976             lockedBody: body,
56977             body: body,
56978             lockedHeader: header[0],
56979             header: header[1]
56980         });
56981
56982         //this.updateColumns();
56983
56984         this.grid.getGridEl().dom.innerHTML = html;
56985
56986         this.initElements();
56987         
56988         // a kludge to fix the random scolling effect in webkit
56989         this.el.on("scroll", function() {
56990             this.el.dom.scrollTop=0; // hopefully not recursive..
56991         },this);
56992
56993         this.scroller.on("scroll", this.handleScroll, this);
56994         this.lockedBody.on("mousewheel", this.handleWheel, this);
56995         this.mainBody.on("mousewheel", this.handleWheel, this);
56996
56997         this.mainHd.on("mouseover", this.handleHdOver, this);
56998         this.mainHd.on("mouseout", this.handleHdOut, this);
56999         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57000                 {delegate: "."+this.splitClass});
57001
57002         this.lockedHd.on("mouseover", this.handleHdOver, this);
57003         this.lockedHd.on("mouseout", this.handleHdOut, this);
57004         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57005                 {delegate: "."+this.splitClass});
57006
57007         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57008             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57009         }
57010
57011         this.updateSplitters();
57012
57013         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57014             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57015             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57016         }
57017
57018         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57019             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57020             this.hmenu.add(
57021                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57022                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57023             );
57024             if(this.grid.enableColLock !== false){
57025                 this.hmenu.add('-',
57026                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57027                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57028                 );
57029             }
57030             if (Roo.isTouch) {
57031                  this.hmenu.add('-',
57032                     {id:"wider", text: this.columnsWiderText},
57033                     {id:"narrow", text: this.columnsNarrowText }
57034                 );
57035                 
57036                  
57037             }
57038             
57039             if(this.grid.enableColumnHide !== false){
57040
57041                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57042                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57043                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57044
57045                 this.hmenu.add('-',
57046                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57047                 );
57048             }
57049             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57050
57051             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57052         }
57053
57054         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57055             this.dd = new Roo.grid.GridDragZone(this.grid, {
57056                 ddGroup : this.grid.ddGroup || 'GridDD'
57057             });
57058             
57059         }
57060
57061         /*
57062         for(var i = 0; i < colCount; i++){
57063             if(cm.isHidden(i)){
57064                 this.hideColumn(i);
57065             }
57066             if(cm.config[i].align){
57067                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57068                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57069             }
57070         }*/
57071         
57072         this.updateHeaderSortState();
57073
57074         this.beforeInitialResize();
57075         this.layout(true);
57076
57077         // two part rendering gives faster view to the user
57078         this.renderPhase2.defer(1, this);
57079     },
57080
57081     renderPhase2 : function(){
57082         // render the rows now
57083         this.refresh();
57084         if(this.grid.autoSizeColumns){
57085             this.autoSizeColumns();
57086         }
57087     },
57088
57089     beforeInitialResize : function(){
57090
57091     },
57092
57093     onColumnSplitterMoved : function(i, w){
57094         this.userResized = true;
57095         var cm = this.grid.colModel;
57096         cm.setColumnWidth(i, w, true);
57097         var cid = cm.getColumnId(i);
57098         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57099         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57100         this.updateSplitters();
57101         this.layout();
57102         this.grid.fireEvent("columnresize", i, w);
57103     },
57104
57105     syncRowHeights : function(startIndex, endIndex){
57106         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57107             startIndex = startIndex || 0;
57108             var mrows = this.getBodyTable().rows;
57109             var lrows = this.getLockedTable().rows;
57110             var len = mrows.length-1;
57111             endIndex = Math.min(endIndex || len, len);
57112             for(var i = startIndex; i <= endIndex; i++){
57113                 var m = mrows[i], l = lrows[i];
57114                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57115                 m.style.height = l.style.height = h + "px";
57116             }
57117         }
57118     },
57119
57120     layout : function(initialRender, is2ndPass){
57121         var g = this.grid;
57122         var auto = g.autoHeight;
57123         var scrollOffset = 16;
57124         var c = g.getGridEl(), cm = this.cm,
57125                 expandCol = g.autoExpandColumn,
57126                 gv = this;
57127         //c.beginMeasure();
57128
57129         if(!c.dom.offsetWidth){ // display:none?
57130             if(initialRender){
57131                 this.lockedWrap.show();
57132                 this.mainWrap.show();
57133             }
57134             return;
57135         }
57136
57137         var hasLock = this.cm.isLocked(0);
57138
57139         var tbh = this.headerPanel.getHeight();
57140         var bbh = this.footerPanel.getHeight();
57141
57142         if(auto){
57143             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57144             var newHeight = ch + c.getBorderWidth("tb");
57145             if(g.maxHeight){
57146                 newHeight = Math.min(g.maxHeight, newHeight);
57147             }
57148             c.setHeight(newHeight);
57149         }
57150
57151         if(g.autoWidth){
57152             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57153         }
57154
57155         var s = this.scroller;
57156
57157         var csize = c.getSize(true);
57158
57159         this.el.setSize(csize.width, csize.height);
57160
57161         this.headerPanel.setWidth(csize.width);
57162         this.footerPanel.setWidth(csize.width);
57163
57164         var hdHeight = this.mainHd.getHeight();
57165         var vw = csize.width;
57166         var vh = csize.height - (tbh + bbh);
57167
57168         s.setSize(vw, vh);
57169
57170         var bt = this.getBodyTable();
57171         
57172         if(cm.getLockedCount() == cm.config.length){
57173             bt = this.getLockedTable();
57174         }
57175         
57176         var ltWidth = hasLock ?
57177                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57178
57179         var scrollHeight = bt.offsetHeight;
57180         var scrollWidth = ltWidth + bt.offsetWidth;
57181         var vscroll = false, hscroll = false;
57182
57183         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57184
57185         var lw = this.lockedWrap, mw = this.mainWrap;
57186         var lb = this.lockedBody, mb = this.mainBody;
57187
57188         setTimeout(function(){
57189             var t = s.dom.offsetTop;
57190             var w = s.dom.clientWidth,
57191                 h = s.dom.clientHeight;
57192
57193             lw.setTop(t);
57194             lw.setSize(ltWidth, h);
57195
57196             mw.setLeftTop(ltWidth, t);
57197             mw.setSize(w-ltWidth, h);
57198
57199             lb.setHeight(h-hdHeight);
57200             mb.setHeight(h-hdHeight);
57201
57202             if(is2ndPass !== true && !gv.userResized && expandCol){
57203                 // high speed resize without full column calculation
57204                 
57205                 var ci = cm.getIndexById(expandCol);
57206                 if (ci < 0) {
57207                     ci = cm.findColumnIndex(expandCol);
57208                 }
57209                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57210                 var expandId = cm.getColumnId(ci);
57211                 var  tw = cm.getTotalWidth(false);
57212                 var currentWidth = cm.getColumnWidth(ci);
57213                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57214                 if(currentWidth != cw){
57215                     cm.setColumnWidth(ci, cw, true);
57216                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57217                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57218                     gv.updateSplitters();
57219                     gv.layout(false, true);
57220                 }
57221             }
57222
57223             if(initialRender){
57224                 lw.show();
57225                 mw.show();
57226             }
57227             //c.endMeasure();
57228         }, 10);
57229     },
57230
57231     onWindowResize : function(){
57232         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57233             return;
57234         }
57235         this.layout();
57236     },
57237
57238     appendFooter : function(parentEl){
57239         return null;
57240     },
57241
57242     sortAscText : "Sort Ascending",
57243     sortDescText : "Sort Descending",
57244     lockText : "Lock Column",
57245     unlockText : "Unlock Column",
57246     columnsText : "Columns",
57247  
57248     columnsWiderText : "Wider",
57249     columnsNarrowText : "Thinner"
57250 });
57251
57252
57253 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57254     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57255     this.proxy.el.addClass('x-grid3-col-dd');
57256 };
57257
57258 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57259     handleMouseDown : function(e){
57260
57261     },
57262
57263     callHandleMouseDown : function(e){
57264         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57265     }
57266 });
57267 /*
57268  * Based on:
57269  * Ext JS Library 1.1.1
57270  * Copyright(c) 2006-2007, Ext JS, LLC.
57271  *
57272  * Originally Released Under LGPL - original licence link has changed is not relivant.
57273  *
57274  * Fork - LGPL
57275  * <script type="text/javascript">
57276  */
57277  
57278 // private
57279 // This is a support class used internally by the Grid components
57280 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57281     this.grid = grid;
57282     this.view = grid.getView();
57283     this.proxy = this.view.resizeProxy;
57284     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57285         "gridSplitters" + this.grid.getGridEl().id, {
57286         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57287     });
57288     this.setHandleElId(Roo.id(hd));
57289     this.setOuterHandleElId(Roo.id(hd2));
57290     this.scroll = false;
57291 };
57292 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57293     fly: Roo.Element.fly,
57294
57295     b4StartDrag : function(x, y){
57296         this.view.headersDisabled = true;
57297         this.proxy.setHeight(this.view.mainWrap.getHeight());
57298         var w = this.cm.getColumnWidth(this.cellIndex);
57299         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57300         this.resetConstraints();
57301         this.setXConstraint(minw, 1000);
57302         this.setYConstraint(0, 0);
57303         this.minX = x - minw;
57304         this.maxX = x + 1000;
57305         this.startPos = x;
57306         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57307     },
57308
57309
57310     handleMouseDown : function(e){
57311         ev = Roo.EventObject.setEvent(e);
57312         var t = this.fly(ev.getTarget());
57313         if(t.hasClass("x-grid-split")){
57314             this.cellIndex = this.view.getCellIndex(t.dom);
57315             this.split = t.dom;
57316             this.cm = this.grid.colModel;
57317             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57318                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57319             }
57320         }
57321     },
57322
57323     endDrag : function(e){
57324         this.view.headersDisabled = false;
57325         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57326         var diff = endX - this.startPos;
57327         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57328     },
57329
57330     autoOffset : function(){
57331         this.setDelta(0,0);
57332     }
57333 });/*
57334  * Based on:
57335  * Ext JS Library 1.1.1
57336  * Copyright(c) 2006-2007, Ext JS, LLC.
57337  *
57338  * Originally Released Under LGPL - original licence link has changed is not relivant.
57339  *
57340  * Fork - LGPL
57341  * <script type="text/javascript">
57342  */
57343  
57344 // private
57345 // This is a support class used internally by the Grid components
57346 Roo.grid.GridDragZone = function(grid, config){
57347     this.view = grid.getView();
57348     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57349     if(this.view.lockedBody){
57350         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57351         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57352     }
57353     this.scroll = false;
57354     this.grid = grid;
57355     this.ddel = document.createElement('div');
57356     this.ddel.className = 'x-grid-dd-wrap';
57357 };
57358
57359 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57360     ddGroup : "GridDD",
57361
57362     getDragData : function(e){
57363         var t = Roo.lib.Event.getTarget(e);
57364         var rowIndex = this.view.findRowIndex(t);
57365         var sm = this.grid.selModel;
57366             
57367         //Roo.log(rowIndex);
57368         
57369         if (sm.getSelectedCell) {
57370             // cell selection..
57371             if (!sm.getSelectedCell()) {
57372                 return false;
57373             }
57374             if (rowIndex != sm.getSelectedCell()[0]) {
57375                 return false;
57376             }
57377         
57378         }
57379         
57380         if(rowIndex !== false){
57381             
57382             // if editorgrid.. 
57383             
57384             
57385             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57386                
57387             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57388               //  
57389             //}
57390             if (e.hasModifier()){
57391                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57392             }
57393             
57394             Roo.log("getDragData");
57395             
57396             return {
57397                 grid: this.grid,
57398                 ddel: this.ddel,
57399                 rowIndex: rowIndex,
57400                 selections:sm.getSelections ? sm.getSelections() : (
57401                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57402                 )
57403             };
57404         }
57405         return false;
57406     },
57407
57408     onInitDrag : function(e){
57409         var data = this.dragData;
57410         this.ddel.innerHTML = this.grid.getDragDropText();
57411         this.proxy.update(this.ddel);
57412         // fire start drag?
57413     },
57414
57415     afterRepair : function(){
57416         this.dragging = false;
57417     },
57418
57419     getRepairXY : function(e, data){
57420         return false;
57421     },
57422
57423     onEndDrag : function(data, e){
57424         // fire end drag?
57425     },
57426
57427     onValidDrop : function(dd, e, id){
57428         // fire drag drop?
57429         this.hideProxy();
57430     },
57431
57432     beforeInvalidDrop : function(e, id){
57433
57434     }
57435 });/*
57436  * Based on:
57437  * Ext JS Library 1.1.1
57438  * Copyright(c) 2006-2007, Ext JS, LLC.
57439  *
57440  * Originally Released Under LGPL - original licence link has changed is not relivant.
57441  *
57442  * Fork - LGPL
57443  * <script type="text/javascript">
57444  */
57445  
57446
57447 /**
57448  * @class Roo.grid.ColumnModel
57449  * @extends Roo.util.Observable
57450  * This is the default implementation of a ColumnModel used by the Grid. It defines
57451  * the columns in the grid.
57452  * <br>Usage:<br>
57453  <pre><code>
57454  var colModel = new Roo.grid.ColumnModel([
57455         {header: "Ticker", width: 60, sortable: true, locked: true},
57456         {header: "Company Name", width: 150, sortable: true},
57457         {header: "Market Cap.", width: 100, sortable: true},
57458         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57459         {header: "Employees", width: 100, sortable: true, resizable: false}
57460  ]);
57461  </code></pre>
57462  * <p>
57463  
57464  * The config options listed for this class are options which may appear in each
57465  * individual column definition.
57466  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57467  * @constructor
57468  * @param {Object} config An Array of column config objects. See this class's
57469  * config objects for details.
57470 */
57471 Roo.grid.ColumnModel = function(config){
57472         /**
57473      * The config passed into the constructor
57474      */
57475     this.config = config;
57476     this.lookup = {};
57477
57478     // if no id, create one
57479     // if the column does not have a dataIndex mapping,
57480     // map it to the order it is in the config
57481     for(var i = 0, len = config.length; i < len; i++){
57482         var c = config[i];
57483         if(typeof c.dataIndex == "undefined"){
57484             c.dataIndex = i;
57485         }
57486         if(typeof c.renderer == "string"){
57487             c.renderer = Roo.util.Format[c.renderer];
57488         }
57489         if(typeof c.id == "undefined"){
57490             c.id = Roo.id();
57491         }
57492         if(c.editor && c.editor.xtype){
57493             c.editor  = Roo.factory(c.editor, Roo.grid);
57494         }
57495         if(c.editor && c.editor.isFormField){
57496             c.editor = new Roo.grid.GridEditor(c.editor);
57497         }
57498         this.lookup[c.id] = c;
57499     }
57500
57501     /**
57502      * The width of columns which have no width specified (defaults to 100)
57503      * @type Number
57504      */
57505     this.defaultWidth = 100;
57506
57507     /**
57508      * Default sortable of columns which have no sortable specified (defaults to false)
57509      * @type Boolean
57510      */
57511     this.defaultSortable = false;
57512
57513     this.addEvents({
57514         /**
57515              * @event widthchange
57516              * Fires when the width of a column changes.
57517              * @param {ColumnModel} this
57518              * @param {Number} columnIndex The column index
57519              * @param {Number} newWidth The new width
57520              */
57521             "widthchange": true,
57522         /**
57523              * @event headerchange
57524              * Fires when the text of a header changes.
57525              * @param {ColumnModel} this
57526              * @param {Number} columnIndex The column index
57527              * @param {Number} newText The new header text
57528              */
57529             "headerchange": true,
57530         /**
57531              * @event hiddenchange
57532              * Fires when a column is hidden or "unhidden".
57533              * @param {ColumnModel} this
57534              * @param {Number} columnIndex The column index
57535              * @param {Boolean} hidden true if hidden, false otherwise
57536              */
57537             "hiddenchange": true,
57538             /**
57539          * @event columnmoved
57540          * Fires when a column is moved.
57541          * @param {ColumnModel} this
57542          * @param {Number} oldIndex
57543          * @param {Number} newIndex
57544          */
57545         "columnmoved" : true,
57546         /**
57547          * @event columlockchange
57548          * Fires when a column's locked state is changed
57549          * @param {ColumnModel} this
57550          * @param {Number} colIndex
57551          * @param {Boolean} locked true if locked
57552          */
57553         "columnlockchange" : true
57554     });
57555     Roo.grid.ColumnModel.superclass.constructor.call(this);
57556 };
57557 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57558     /**
57559      * @cfg {String} header The header text to display in the Grid view.
57560      */
57561     /**
57562      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57563      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57564      * specified, the column's index is used as an index into the Record's data Array.
57565      */
57566     /**
57567      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57568      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57569      */
57570     /**
57571      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57572      * Defaults to the value of the {@link #defaultSortable} property.
57573      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57574      */
57575     /**
57576      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57577      */
57578     /**
57579      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57580      */
57581     /**
57582      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57583      */
57584     /**
57585      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57586      */
57587     /**
57588      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57589      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57590      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57591      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57592      */
57593        /**
57594      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57595      */
57596     /**
57597      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57598      */
57599     /**
57600      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57601      */
57602     /**
57603      * @cfg {String} cursor (Optional)
57604      */
57605     /**
57606      * @cfg {String} tooltip (Optional)
57607      */
57608     /**
57609      * @cfg {Number} xs (Optional)
57610      */
57611     /**
57612      * @cfg {Number} sm (Optional)
57613      */
57614     /**
57615      * @cfg {Number} md (Optional)
57616      */
57617     /**
57618      * @cfg {Number} lg (Optional)
57619      */
57620     /**
57621      * Returns the id of the column at the specified index.
57622      * @param {Number} index The column index
57623      * @return {String} the id
57624      */
57625     getColumnId : function(index){
57626         return this.config[index].id;
57627     },
57628
57629     /**
57630      * Returns the column for a specified id.
57631      * @param {String} id The column id
57632      * @return {Object} the column
57633      */
57634     getColumnById : function(id){
57635         return this.lookup[id];
57636     },
57637
57638     
57639     /**
57640      * Returns the column for a specified dataIndex.
57641      * @param {String} dataIndex The column dataIndex
57642      * @return {Object|Boolean} the column or false if not found
57643      */
57644     getColumnByDataIndex: function(dataIndex){
57645         var index = this.findColumnIndex(dataIndex);
57646         return index > -1 ? this.config[index] : false;
57647     },
57648     
57649     /**
57650      * Returns the index for a specified column id.
57651      * @param {String} id The column id
57652      * @return {Number} the index, or -1 if not found
57653      */
57654     getIndexById : function(id){
57655         for(var i = 0, len = this.config.length; i < len; i++){
57656             if(this.config[i].id == id){
57657                 return i;
57658             }
57659         }
57660         return -1;
57661     },
57662     
57663     /**
57664      * Returns the index for a specified column dataIndex.
57665      * @param {String} dataIndex The column dataIndex
57666      * @return {Number} the index, or -1 if not found
57667      */
57668     
57669     findColumnIndex : function(dataIndex){
57670         for(var i = 0, len = this.config.length; i < len; i++){
57671             if(this.config[i].dataIndex == dataIndex){
57672                 return i;
57673             }
57674         }
57675         return -1;
57676     },
57677     
57678     
57679     moveColumn : function(oldIndex, newIndex){
57680         var c = this.config[oldIndex];
57681         this.config.splice(oldIndex, 1);
57682         this.config.splice(newIndex, 0, c);
57683         this.dataMap = null;
57684         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57685     },
57686
57687     isLocked : function(colIndex){
57688         return this.config[colIndex].locked === true;
57689     },
57690
57691     setLocked : function(colIndex, value, suppressEvent){
57692         if(this.isLocked(colIndex) == value){
57693             return;
57694         }
57695         this.config[colIndex].locked = value;
57696         if(!suppressEvent){
57697             this.fireEvent("columnlockchange", this, colIndex, value);
57698         }
57699     },
57700
57701     getTotalLockedWidth : function(){
57702         var totalWidth = 0;
57703         for(var i = 0; i < this.config.length; i++){
57704             if(this.isLocked(i) && !this.isHidden(i)){
57705                 this.totalWidth += this.getColumnWidth(i);
57706             }
57707         }
57708         return totalWidth;
57709     },
57710
57711     getLockedCount : function(){
57712         for(var i = 0, len = this.config.length; i < len; i++){
57713             if(!this.isLocked(i)){
57714                 return i;
57715             }
57716         }
57717         
57718         return this.config.length;
57719     },
57720
57721     /**
57722      * Returns the number of columns.
57723      * @return {Number}
57724      */
57725     getColumnCount : function(visibleOnly){
57726         if(visibleOnly === true){
57727             var c = 0;
57728             for(var i = 0, len = this.config.length; i < len; i++){
57729                 if(!this.isHidden(i)){
57730                     c++;
57731                 }
57732             }
57733             return c;
57734         }
57735         return this.config.length;
57736     },
57737
57738     /**
57739      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57740      * @param {Function} fn
57741      * @param {Object} scope (optional)
57742      * @return {Array} result
57743      */
57744     getColumnsBy : function(fn, scope){
57745         var r = [];
57746         for(var i = 0, len = this.config.length; i < len; i++){
57747             var c = this.config[i];
57748             if(fn.call(scope||this, c, i) === true){
57749                 r[r.length] = c;
57750             }
57751         }
57752         return r;
57753     },
57754
57755     /**
57756      * Returns true if the specified column is sortable.
57757      * @param {Number} col The column index
57758      * @return {Boolean}
57759      */
57760     isSortable : function(col){
57761         if(typeof this.config[col].sortable == "undefined"){
57762             return this.defaultSortable;
57763         }
57764         return this.config[col].sortable;
57765     },
57766
57767     /**
57768      * Returns the rendering (formatting) function defined for the column.
57769      * @param {Number} col The column index.
57770      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57771      */
57772     getRenderer : function(col){
57773         if(!this.config[col].renderer){
57774             return Roo.grid.ColumnModel.defaultRenderer;
57775         }
57776         return this.config[col].renderer;
57777     },
57778
57779     /**
57780      * Sets the rendering (formatting) function for a column.
57781      * @param {Number} col The column index
57782      * @param {Function} fn The function to use to process the cell's raw data
57783      * to return HTML markup for the grid view. The render function is called with
57784      * the following parameters:<ul>
57785      * <li>Data value.</li>
57786      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57787      * <li>css A CSS style string to apply to the table cell.</li>
57788      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57789      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57790      * <li>Row index</li>
57791      * <li>Column index</li>
57792      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57793      */
57794     setRenderer : function(col, fn){
57795         this.config[col].renderer = fn;
57796     },
57797
57798     /**
57799      * Returns the width for the specified column.
57800      * @param {Number} col The column index
57801      * @return {Number}
57802      */
57803     getColumnWidth : function(col){
57804         return this.config[col].width * 1 || this.defaultWidth;
57805     },
57806
57807     /**
57808      * Sets the width for a column.
57809      * @param {Number} col The column index
57810      * @param {Number} width The new width
57811      */
57812     setColumnWidth : function(col, width, suppressEvent){
57813         this.config[col].width = width;
57814         this.totalWidth = null;
57815         if(!suppressEvent){
57816              this.fireEvent("widthchange", this, col, width);
57817         }
57818     },
57819
57820     /**
57821      * Returns the total width of all columns.
57822      * @param {Boolean} includeHidden True to include hidden column widths
57823      * @return {Number}
57824      */
57825     getTotalWidth : function(includeHidden){
57826         if(!this.totalWidth){
57827             this.totalWidth = 0;
57828             for(var i = 0, len = this.config.length; i < len; i++){
57829                 if(includeHidden || !this.isHidden(i)){
57830                     this.totalWidth += this.getColumnWidth(i);
57831                 }
57832             }
57833         }
57834         return this.totalWidth;
57835     },
57836
57837     /**
57838      * Returns the header for the specified column.
57839      * @param {Number} col The column index
57840      * @return {String}
57841      */
57842     getColumnHeader : function(col){
57843         return this.config[col].header;
57844     },
57845
57846     /**
57847      * Sets the header for a column.
57848      * @param {Number} col The column index
57849      * @param {String} header The new header
57850      */
57851     setColumnHeader : function(col, header){
57852         this.config[col].header = header;
57853         this.fireEvent("headerchange", this, col, header);
57854     },
57855
57856     /**
57857      * Returns the tooltip for the specified column.
57858      * @param {Number} col The column index
57859      * @return {String}
57860      */
57861     getColumnTooltip : function(col){
57862             return this.config[col].tooltip;
57863     },
57864     /**
57865      * Sets the tooltip for a column.
57866      * @param {Number} col The column index
57867      * @param {String} tooltip The new tooltip
57868      */
57869     setColumnTooltip : function(col, tooltip){
57870             this.config[col].tooltip = tooltip;
57871     },
57872
57873     /**
57874      * Returns the dataIndex for the specified column.
57875      * @param {Number} col The column index
57876      * @return {Number}
57877      */
57878     getDataIndex : function(col){
57879         return this.config[col].dataIndex;
57880     },
57881
57882     /**
57883      * Sets the dataIndex for a column.
57884      * @param {Number} col The column index
57885      * @param {Number} dataIndex The new dataIndex
57886      */
57887     setDataIndex : function(col, dataIndex){
57888         this.config[col].dataIndex = dataIndex;
57889     },
57890
57891     
57892     
57893     /**
57894      * Returns true if the cell is editable.
57895      * @param {Number} colIndex The column index
57896      * @param {Number} rowIndex The row index - this is nto actually used..?
57897      * @return {Boolean}
57898      */
57899     isCellEditable : function(colIndex, rowIndex){
57900         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57901     },
57902
57903     /**
57904      * Returns the editor defined for the cell/column.
57905      * return false or null to disable editing.
57906      * @param {Number} colIndex The column index
57907      * @param {Number} rowIndex The row index
57908      * @return {Object}
57909      */
57910     getCellEditor : function(colIndex, rowIndex){
57911         return this.config[colIndex].editor;
57912     },
57913
57914     /**
57915      * Sets if a column is editable.
57916      * @param {Number} col The column index
57917      * @param {Boolean} editable True if the column is editable
57918      */
57919     setEditable : function(col, editable){
57920         this.config[col].editable = editable;
57921     },
57922
57923
57924     /**
57925      * Returns true if the column is hidden.
57926      * @param {Number} colIndex The column index
57927      * @return {Boolean}
57928      */
57929     isHidden : function(colIndex){
57930         return this.config[colIndex].hidden;
57931     },
57932
57933
57934     /**
57935      * Returns true if the column width cannot be changed
57936      */
57937     isFixed : function(colIndex){
57938         return this.config[colIndex].fixed;
57939     },
57940
57941     /**
57942      * Returns true if the column can be resized
57943      * @return {Boolean}
57944      */
57945     isResizable : function(colIndex){
57946         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57947     },
57948     /**
57949      * Sets if a column is hidden.
57950      * @param {Number} colIndex The column index
57951      * @param {Boolean} hidden True if the column is hidden
57952      */
57953     setHidden : function(colIndex, hidden){
57954         this.config[colIndex].hidden = hidden;
57955         this.totalWidth = null;
57956         this.fireEvent("hiddenchange", this, colIndex, hidden);
57957     },
57958
57959     /**
57960      * Sets the editor for a column.
57961      * @param {Number} col The column index
57962      * @param {Object} editor The editor object
57963      */
57964     setEditor : function(col, editor){
57965         this.config[col].editor = editor;
57966     }
57967 });
57968
57969 Roo.grid.ColumnModel.defaultRenderer = function(value)
57970 {
57971     if(typeof value == "object") {
57972         return value;
57973     }
57974         if(typeof value == "string" && value.length < 1){
57975             return "&#160;";
57976         }
57977     
57978         return String.format("{0}", value);
57979 };
57980
57981 // Alias for backwards compatibility
57982 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57983 /*
57984  * Based on:
57985  * Ext JS Library 1.1.1
57986  * Copyright(c) 2006-2007, Ext JS, LLC.
57987  *
57988  * Originally Released Under LGPL - original licence link has changed is not relivant.
57989  *
57990  * Fork - LGPL
57991  * <script type="text/javascript">
57992  */
57993
57994 /**
57995  * @class Roo.grid.AbstractSelectionModel
57996  * @extends Roo.util.Observable
57997  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57998  * implemented by descendant classes.  This class should not be directly instantiated.
57999  * @constructor
58000  */
58001 Roo.grid.AbstractSelectionModel = function(){
58002     this.locked = false;
58003     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58004 };
58005
58006 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58007     /** @ignore Called by the grid automatically. Do not call directly. */
58008     init : function(grid){
58009         this.grid = grid;
58010         this.initEvents();
58011     },
58012
58013     /**
58014      * Locks the selections.
58015      */
58016     lock : function(){
58017         this.locked = true;
58018     },
58019
58020     /**
58021      * Unlocks the selections.
58022      */
58023     unlock : function(){
58024         this.locked = false;
58025     },
58026
58027     /**
58028      * Returns true if the selections are locked.
58029      * @return {Boolean}
58030      */
58031     isLocked : function(){
58032         return this.locked;
58033     }
58034 });/*
58035  * Based on:
58036  * Ext JS Library 1.1.1
58037  * Copyright(c) 2006-2007, Ext JS, LLC.
58038  *
58039  * Originally Released Under LGPL - original licence link has changed is not relivant.
58040  *
58041  * Fork - LGPL
58042  * <script type="text/javascript">
58043  */
58044 /**
58045  * @extends Roo.grid.AbstractSelectionModel
58046  * @class Roo.grid.RowSelectionModel
58047  * The default SelectionModel used by {@link Roo.grid.Grid}.
58048  * It supports multiple selections and keyboard selection/navigation. 
58049  * @constructor
58050  * @param {Object} config
58051  */
58052 Roo.grid.RowSelectionModel = function(config){
58053     Roo.apply(this, config);
58054     this.selections = new Roo.util.MixedCollection(false, function(o){
58055         return o.id;
58056     });
58057
58058     this.last = false;
58059     this.lastActive = false;
58060
58061     this.addEvents({
58062         /**
58063              * @event selectionchange
58064              * Fires when the selection changes
58065              * @param {SelectionModel} this
58066              */
58067             "selectionchange" : true,
58068         /**
58069              * @event afterselectionchange
58070              * Fires after the selection changes (eg. by key press or clicking)
58071              * @param {SelectionModel} this
58072              */
58073             "afterselectionchange" : true,
58074         /**
58075              * @event beforerowselect
58076              * Fires when a row is selected being selected, return false to cancel.
58077              * @param {SelectionModel} this
58078              * @param {Number} rowIndex The selected index
58079              * @param {Boolean} keepExisting False if other selections will be cleared
58080              */
58081             "beforerowselect" : true,
58082         /**
58083              * @event rowselect
58084              * Fires when a row is selected.
58085              * @param {SelectionModel} this
58086              * @param {Number} rowIndex The selected index
58087              * @param {Roo.data.Record} r The record
58088              */
58089             "rowselect" : true,
58090         /**
58091              * @event rowdeselect
58092              * Fires when a row is deselected.
58093              * @param {SelectionModel} this
58094              * @param {Number} rowIndex The selected index
58095              */
58096         "rowdeselect" : true
58097     });
58098     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58099     this.locked = false;
58100 };
58101
58102 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58103     /**
58104      * @cfg {Boolean} singleSelect
58105      * True to allow selection of only one row at a time (defaults to false)
58106      */
58107     singleSelect : false,
58108
58109     // private
58110     initEvents : function(){
58111
58112         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58113             this.grid.on("mousedown", this.handleMouseDown, this);
58114         }else{ // allow click to work like normal
58115             this.grid.on("rowclick", this.handleDragableRowClick, this);
58116         }
58117
58118         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58119             "up" : function(e){
58120                 if(!e.shiftKey){
58121                     this.selectPrevious(e.shiftKey);
58122                 }else if(this.last !== false && this.lastActive !== false){
58123                     var last = this.last;
58124                     this.selectRange(this.last,  this.lastActive-1);
58125                     this.grid.getView().focusRow(this.lastActive);
58126                     if(last !== false){
58127                         this.last = last;
58128                     }
58129                 }else{
58130                     this.selectFirstRow();
58131                 }
58132                 this.fireEvent("afterselectionchange", this);
58133             },
58134             "down" : function(e){
58135                 if(!e.shiftKey){
58136                     this.selectNext(e.shiftKey);
58137                 }else if(this.last !== false && this.lastActive !== false){
58138                     var last = this.last;
58139                     this.selectRange(this.last,  this.lastActive+1);
58140                     this.grid.getView().focusRow(this.lastActive);
58141                     if(last !== false){
58142                         this.last = last;
58143                     }
58144                 }else{
58145                     this.selectFirstRow();
58146                 }
58147                 this.fireEvent("afterselectionchange", this);
58148             },
58149             scope: this
58150         });
58151
58152         var view = this.grid.view;
58153         view.on("refresh", this.onRefresh, this);
58154         view.on("rowupdated", this.onRowUpdated, this);
58155         view.on("rowremoved", this.onRemove, this);
58156     },
58157
58158     // private
58159     onRefresh : function(){
58160         var ds = this.grid.dataSource, i, v = this.grid.view;
58161         var s = this.selections;
58162         s.each(function(r){
58163             if((i = ds.indexOfId(r.id)) != -1){
58164                 v.onRowSelect(i);
58165                 s.add(ds.getAt(i)); // updating the selection relate data
58166             }else{
58167                 s.remove(r);
58168             }
58169         });
58170     },
58171
58172     // private
58173     onRemove : function(v, index, r){
58174         this.selections.remove(r);
58175     },
58176
58177     // private
58178     onRowUpdated : function(v, index, r){
58179         if(this.isSelected(r)){
58180             v.onRowSelect(index);
58181         }
58182     },
58183
58184     /**
58185      * Select records.
58186      * @param {Array} records The records to select
58187      * @param {Boolean} keepExisting (optional) True to keep existing selections
58188      */
58189     selectRecords : function(records, keepExisting){
58190         if(!keepExisting){
58191             this.clearSelections();
58192         }
58193         var ds = this.grid.dataSource;
58194         for(var i = 0, len = records.length; i < len; i++){
58195             this.selectRow(ds.indexOf(records[i]), true);
58196         }
58197     },
58198
58199     /**
58200      * Gets the number of selected rows.
58201      * @return {Number}
58202      */
58203     getCount : function(){
58204         return this.selections.length;
58205     },
58206
58207     /**
58208      * Selects the first row in the grid.
58209      */
58210     selectFirstRow : function(){
58211         this.selectRow(0);
58212     },
58213
58214     /**
58215      * Select the last row.
58216      * @param {Boolean} keepExisting (optional) True to keep existing selections
58217      */
58218     selectLastRow : function(keepExisting){
58219         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58220     },
58221
58222     /**
58223      * Selects the row immediately following the last selected row.
58224      * @param {Boolean} keepExisting (optional) True to keep existing selections
58225      */
58226     selectNext : function(keepExisting){
58227         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58228             this.selectRow(this.last+1, keepExisting);
58229             this.grid.getView().focusRow(this.last);
58230         }
58231     },
58232
58233     /**
58234      * Selects the row that precedes the last selected row.
58235      * @param {Boolean} keepExisting (optional) True to keep existing selections
58236      */
58237     selectPrevious : function(keepExisting){
58238         if(this.last){
58239             this.selectRow(this.last-1, keepExisting);
58240             this.grid.getView().focusRow(this.last);
58241         }
58242     },
58243
58244     /**
58245      * Returns the selected records
58246      * @return {Array} Array of selected records
58247      */
58248     getSelections : function(){
58249         return [].concat(this.selections.items);
58250     },
58251
58252     /**
58253      * Returns the first selected record.
58254      * @return {Record}
58255      */
58256     getSelected : function(){
58257         return this.selections.itemAt(0);
58258     },
58259
58260
58261     /**
58262      * Clears all selections.
58263      */
58264     clearSelections : function(fast){
58265         if(this.locked) {
58266             return;
58267         }
58268         if(fast !== true){
58269             var ds = this.grid.dataSource;
58270             var s = this.selections;
58271             s.each(function(r){
58272                 this.deselectRow(ds.indexOfId(r.id));
58273             }, this);
58274             s.clear();
58275         }else{
58276             this.selections.clear();
58277         }
58278         this.last = false;
58279     },
58280
58281
58282     /**
58283      * Selects all rows.
58284      */
58285     selectAll : function(){
58286         if(this.locked) {
58287             return;
58288         }
58289         this.selections.clear();
58290         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58291             this.selectRow(i, true);
58292         }
58293     },
58294
58295     /**
58296      * Returns True if there is a selection.
58297      * @return {Boolean}
58298      */
58299     hasSelection : function(){
58300         return this.selections.length > 0;
58301     },
58302
58303     /**
58304      * Returns True if the specified row is selected.
58305      * @param {Number/Record} record The record or index of the record to check
58306      * @return {Boolean}
58307      */
58308     isSelected : function(index){
58309         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58310         return (r && this.selections.key(r.id) ? true : false);
58311     },
58312
58313     /**
58314      * Returns True if the specified record id is selected.
58315      * @param {String} id The id of record to check
58316      * @return {Boolean}
58317      */
58318     isIdSelected : function(id){
58319         return (this.selections.key(id) ? true : false);
58320     },
58321
58322     // private
58323     handleMouseDown : function(e, t){
58324         var view = this.grid.getView(), rowIndex;
58325         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58326             return;
58327         };
58328         if(e.shiftKey && this.last !== false){
58329             var last = this.last;
58330             this.selectRange(last, rowIndex, e.ctrlKey);
58331             this.last = last; // reset the last
58332             view.focusRow(rowIndex);
58333         }else{
58334             var isSelected = this.isSelected(rowIndex);
58335             if(e.button !== 0 && isSelected){
58336                 view.focusRow(rowIndex);
58337             }else if(e.ctrlKey && isSelected){
58338                 this.deselectRow(rowIndex);
58339             }else if(!isSelected){
58340                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58341                 view.focusRow(rowIndex);
58342             }
58343         }
58344         this.fireEvent("afterselectionchange", this);
58345     },
58346     // private
58347     handleDragableRowClick :  function(grid, rowIndex, e) 
58348     {
58349         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58350             this.selectRow(rowIndex, false);
58351             grid.view.focusRow(rowIndex);
58352              this.fireEvent("afterselectionchange", this);
58353         }
58354     },
58355     
58356     /**
58357      * Selects multiple rows.
58358      * @param {Array} rows Array of the indexes of the row to select
58359      * @param {Boolean} keepExisting (optional) True to keep existing selections
58360      */
58361     selectRows : function(rows, keepExisting){
58362         if(!keepExisting){
58363             this.clearSelections();
58364         }
58365         for(var i = 0, len = rows.length; i < len; i++){
58366             this.selectRow(rows[i], true);
58367         }
58368     },
58369
58370     /**
58371      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58372      * @param {Number} startRow The index of the first row in the range
58373      * @param {Number} endRow The index of the last row in the range
58374      * @param {Boolean} keepExisting (optional) True to retain existing selections
58375      */
58376     selectRange : function(startRow, endRow, keepExisting){
58377         if(this.locked) {
58378             return;
58379         }
58380         if(!keepExisting){
58381             this.clearSelections();
58382         }
58383         if(startRow <= endRow){
58384             for(var i = startRow; i <= endRow; i++){
58385                 this.selectRow(i, true);
58386             }
58387         }else{
58388             for(var i = startRow; i >= endRow; i--){
58389                 this.selectRow(i, true);
58390             }
58391         }
58392     },
58393
58394     /**
58395      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58396      * @param {Number} startRow The index of the first row in the range
58397      * @param {Number} endRow The index of the last row in the range
58398      */
58399     deselectRange : function(startRow, endRow, preventViewNotify){
58400         if(this.locked) {
58401             return;
58402         }
58403         for(var i = startRow; i <= endRow; i++){
58404             this.deselectRow(i, preventViewNotify);
58405         }
58406     },
58407
58408     /**
58409      * Selects a row.
58410      * @param {Number} row The index of the row to select
58411      * @param {Boolean} keepExisting (optional) True to keep existing selections
58412      */
58413     selectRow : function(index, keepExisting, preventViewNotify){
58414         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58415             return;
58416         }
58417         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58418             if(!keepExisting || this.singleSelect){
58419                 this.clearSelections();
58420             }
58421             var r = this.grid.dataSource.getAt(index);
58422             this.selections.add(r);
58423             this.last = this.lastActive = index;
58424             if(!preventViewNotify){
58425                 this.grid.getView().onRowSelect(index);
58426             }
58427             this.fireEvent("rowselect", this, index, r);
58428             this.fireEvent("selectionchange", this);
58429         }
58430     },
58431
58432     /**
58433      * Deselects a row.
58434      * @param {Number} row The index of the row to deselect
58435      */
58436     deselectRow : function(index, preventViewNotify){
58437         if(this.locked) {
58438             return;
58439         }
58440         if(this.last == index){
58441             this.last = false;
58442         }
58443         if(this.lastActive == index){
58444             this.lastActive = false;
58445         }
58446         var r = this.grid.dataSource.getAt(index);
58447         this.selections.remove(r);
58448         if(!preventViewNotify){
58449             this.grid.getView().onRowDeselect(index);
58450         }
58451         this.fireEvent("rowdeselect", this, index);
58452         this.fireEvent("selectionchange", this);
58453     },
58454
58455     // private
58456     restoreLast : function(){
58457         if(this._last){
58458             this.last = this._last;
58459         }
58460     },
58461
58462     // private
58463     acceptsNav : function(row, col, cm){
58464         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58465     },
58466
58467     // private
58468     onEditorKey : function(field, e){
58469         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58470         if(k == e.TAB){
58471             e.stopEvent();
58472             ed.completeEdit();
58473             if(e.shiftKey){
58474                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58475             }else{
58476                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58477             }
58478         }else if(k == e.ENTER && !e.ctrlKey){
58479             e.stopEvent();
58480             ed.completeEdit();
58481             if(e.shiftKey){
58482                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58483             }else{
58484                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58485             }
58486         }else if(k == e.ESC){
58487             ed.cancelEdit();
58488         }
58489         if(newCell){
58490             g.startEditing(newCell[0], newCell[1]);
58491         }
58492     }
58493 });/*
58494  * Based on:
58495  * Ext JS Library 1.1.1
58496  * Copyright(c) 2006-2007, Ext JS, LLC.
58497  *
58498  * Originally Released Under LGPL - original licence link has changed is not relivant.
58499  *
58500  * Fork - LGPL
58501  * <script type="text/javascript">
58502  */
58503 /**
58504  * @class Roo.grid.CellSelectionModel
58505  * @extends Roo.grid.AbstractSelectionModel
58506  * This class provides the basic implementation for cell selection in a grid.
58507  * @constructor
58508  * @param {Object} config The object containing the configuration of this model.
58509  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58510  */
58511 Roo.grid.CellSelectionModel = function(config){
58512     Roo.apply(this, config);
58513
58514     this.selection = null;
58515
58516     this.addEvents({
58517         /**
58518              * @event beforerowselect
58519              * Fires before a cell is selected.
58520              * @param {SelectionModel} this
58521              * @param {Number} rowIndex The selected row index
58522              * @param {Number} colIndex The selected cell index
58523              */
58524             "beforecellselect" : true,
58525         /**
58526              * @event cellselect
58527              * Fires when a cell is selected.
58528              * @param {SelectionModel} this
58529              * @param {Number} rowIndex The selected row index
58530              * @param {Number} colIndex The selected cell index
58531              */
58532             "cellselect" : true,
58533         /**
58534              * @event selectionchange
58535              * Fires when the active selection changes.
58536              * @param {SelectionModel} this
58537              * @param {Object} selection null for no selection or an object (o) with two properties
58538                 <ul>
58539                 <li>o.record: the record object for the row the selection is in</li>
58540                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58541                 </ul>
58542              */
58543             "selectionchange" : true,
58544         /**
58545              * @event tabend
58546              * Fires when the tab (or enter) was pressed on the last editable cell
58547              * You can use this to trigger add new row.
58548              * @param {SelectionModel} this
58549              */
58550             "tabend" : true,
58551          /**
58552              * @event beforeeditnext
58553              * Fires before the next editable sell is made active
58554              * You can use this to skip to another cell or fire the tabend
58555              *    if you set cell to false
58556              * @param {Object} eventdata object : { cell : [ row, col ] } 
58557              */
58558             "beforeeditnext" : true
58559     });
58560     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58561 };
58562
58563 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58564     
58565     enter_is_tab: false,
58566
58567     /** @ignore */
58568     initEvents : function(){
58569         this.grid.on("mousedown", this.handleMouseDown, this);
58570         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58571         var view = this.grid.view;
58572         view.on("refresh", this.onViewChange, this);
58573         view.on("rowupdated", this.onRowUpdated, this);
58574         view.on("beforerowremoved", this.clearSelections, this);
58575         view.on("beforerowsinserted", this.clearSelections, this);
58576         if(this.grid.isEditor){
58577             this.grid.on("beforeedit", this.beforeEdit,  this);
58578         }
58579     },
58580
58581         //private
58582     beforeEdit : function(e){
58583         this.select(e.row, e.column, false, true, e.record);
58584     },
58585
58586         //private
58587     onRowUpdated : function(v, index, r){
58588         if(this.selection && this.selection.record == r){
58589             v.onCellSelect(index, this.selection.cell[1]);
58590         }
58591     },
58592
58593         //private
58594     onViewChange : function(){
58595         this.clearSelections(true);
58596     },
58597
58598         /**
58599          * Returns the currently selected cell,.
58600          * @return {Array} The selected cell (row, column) or null if none selected.
58601          */
58602     getSelectedCell : function(){
58603         return this.selection ? this.selection.cell : null;
58604     },
58605
58606     /**
58607      * Clears all selections.
58608      * @param {Boolean} true to prevent the gridview from being notified about the change.
58609      */
58610     clearSelections : function(preventNotify){
58611         var s = this.selection;
58612         if(s){
58613             if(preventNotify !== true){
58614                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58615             }
58616             this.selection = null;
58617             this.fireEvent("selectionchange", this, null);
58618         }
58619     },
58620
58621     /**
58622      * Returns true if there is a selection.
58623      * @return {Boolean}
58624      */
58625     hasSelection : function(){
58626         return this.selection ? true : false;
58627     },
58628
58629     /** @ignore */
58630     handleMouseDown : function(e, t){
58631         var v = this.grid.getView();
58632         if(this.isLocked()){
58633             return;
58634         };
58635         var row = v.findRowIndex(t);
58636         var cell = v.findCellIndex(t);
58637         if(row !== false && cell !== false){
58638             this.select(row, cell);
58639         }
58640     },
58641
58642     /**
58643      * Selects a cell.
58644      * @param {Number} rowIndex
58645      * @param {Number} collIndex
58646      */
58647     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58648         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58649             this.clearSelections();
58650             r = r || this.grid.dataSource.getAt(rowIndex);
58651             this.selection = {
58652                 record : r,
58653                 cell : [rowIndex, colIndex]
58654             };
58655             if(!preventViewNotify){
58656                 var v = this.grid.getView();
58657                 v.onCellSelect(rowIndex, colIndex);
58658                 if(preventFocus !== true){
58659                     v.focusCell(rowIndex, colIndex);
58660                 }
58661             }
58662             this.fireEvent("cellselect", this, rowIndex, colIndex);
58663             this.fireEvent("selectionchange", this, this.selection);
58664         }
58665     },
58666
58667         //private
58668     isSelectable : function(rowIndex, colIndex, cm){
58669         return !cm.isHidden(colIndex);
58670     },
58671
58672     /** @ignore */
58673     handleKeyDown : function(e){
58674         //Roo.log('Cell Sel Model handleKeyDown');
58675         if(!e.isNavKeyPress()){
58676             return;
58677         }
58678         var g = this.grid, s = this.selection;
58679         if(!s){
58680             e.stopEvent();
58681             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58682             if(cell){
58683                 this.select(cell[0], cell[1]);
58684             }
58685             return;
58686         }
58687         var sm = this;
58688         var walk = function(row, col, step){
58689             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58690         };
58691         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58692         var newCell;
58693
58694       
58695
58696         switch(k){
58697             case e.TAB:
58698                 // handled by onEditorKey
58699                 if (g.isEditor && g.editing) {
58700                     return;
58701                 }
58702                 if(e.shiftKey) {
58703                     newCell = walk(r, c-1, -1);
58704                 } else {
58705                     newCell = walk(r, c+1, 1);
58706                 }
58707                 break;
58708             
58709             case e.DOWN:
58710                newCell = walk(r+1, c, 1);
58711                 break;
58712             
58713             case e.UP:
58714                 newCell = walk(r-1, c, -1);
58715                 break;
58716             
58717             case e.RIGHT:
58718                 newCell = walk(r, c+1, 1);
58719                 break;
58720             
58721             case e.LEFT:
58722                 newCell = walk(r, c-1, -1);
58723                 break;
58724             
58725             case e.ENTER:
58726                 
58727                 if(g.isEditor && !g.editing){
58728                    g.startEditing(r, c);
58729                    e.stopEvent();
58730                    return;
58731                 }
58732                 
58733                 
58734              break;
58735         };
58736         if(newCell){
58737             this.select(newCell[0], newCell[1]);
58738             e.stopEvent();
58739             
58740         }
58741     },
58742
58743     acceptsNav : function(row, col, cm){
58744         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58745     },
58746     /**
58747      * Selects a cell.
58748      * @param {Number} field (not used) - as it's normally used as a listener
58749      * @param {Number} e - event - fake it by using
58750      *
58751      * var e = Roo.EventObjectImpl.prototype;
58752      * e.keyCode = e.TAB
58753      *
58754      * 
58755      */
58756     onEditorKey : function(field, e){
58757         
58758         var k = e.getKey(),
58759             newCell,
58760             g = this.grid,
58761             ed = g.activeEditor,
58762             forward = false;
58763         ///Roo.log('onEditorKey' + k);
58764         
58765         
58766         if (this.enter_is_tab && k == e.ENTER) {
58767             k = e.TAB;
58768         }
58769         
58770         if(k == e.TAB){
58771             if(e.shiftKey){
58772                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58773             }else{
58774                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58775                 forward = true;
58776             }
58777             
58778             e.stopEvent();
58779             
58780         } else if(k == e.ENTER &&  !e.ctrlKey){
58781             ed.completeEdit();
58782             e.stopEvent();
58783             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58784         
58785                 } else if(k == e.ESC){
58786             ed.cancelEdit();
58787         }
58788                 
58789         if (newCell) {
58790             var ecall = { cell : newCell, forward : forward };
58791             this.fireEvent('beforeeditnext', ecall );
58792             newCell = ecall.cell;
58793                         forward = ecall.forward;
58794         }
58795                 
58796         if(newCell){
58797             //Roo.log('next cell after edit');
58798             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58799         } else if (forward) {
58800             // tabbed past last
58801             this.fireEvent.defer(100, this, ['tabend',this]);
58802         }
58803     }
58804 });/*
58805  * Based on:
58806  * Ext JS Library 1.1.1
58807  * Copyright(c) 2006-2007, Ext JS, LLC.
58808  *
58809  * Originally Released Under LGPL - original licence link has changed is not relivant.
58810  *
58811  * Fork - LGPL
58812  * <script type="text/javascript">
58813  */
58814  
58815 /**
58816  * @class Roo.grid.EditorGrid
58817  * @extends Roo.grid.Grid
58818  * Class for creating and editable grid.
58819  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58820  * The container MUST have some type of size defined for the grid to fill. The container will be 
58821  * automatically set to position relative if it isn't already.
58822  * @param {Object} dataSource The data model to bind to
58823  * @param {Object} colModel The column model with info about this grid's columns
58824  */
58825 Roo.grid.EditorGrid = function(container, config){
58826     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58827     this.getGridEl().addClass("xedit-grid");
58828
58829     if(!this.selModel){
58830         this.selModel = new Roo.grid.CellSelectionModel();
58831     }
58832
58833     this.activeEditor = null;
58834
58835         this.addEvents({
58836             /**
58837              * @event beforeedit
58838              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58839              * <ul style="padding:5px;padding-left:16px;">
58840              * <li>grid - This grid</li>
58841              * <li>record - The record being edited</li>
58842              * <li>field - The field name being edited</li>
58843              * <li>value - The value for the field being edited.</li>
58844              * <li>row - The grid row index</li>
58845              * <li>column - The grid column index</li>
58846              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58847              * </ul>
58848              * @param {Object} e An edit event (see above for description)
58849              */
58850             "beforeedit" : true,
58851             /**
58852              * @event afteredit
58853              * Fires after a cell is edited. <br />
58854              * <ul style="padding:5px;padding-left:16px;">
58855              * <li>grid - This grid</li>
58856              * <li>record - The record being edited</li>
58857              * <li>field - The field name being edited</li>
58858              * <li>value - The value being set</li>
58859              * <li>originalValue - The original value for the field, before the edit.</li>
58860              * <li>row - The grid row index</li>
58861              * <li>column - The grid column index</li>
58862              * </ul>
58863              * @param {Object} e An edit event (see above for description)
58864              */
58865             "afteredit" : true,
58866             /**
58867              * @event validateedit
58868              * Fires after a cell is edited, but before the value is set in the record. 
58869          * You can use this to modify the value being set in the field, Return false
58870              * to cancel the change. The edit event object has the following properties <br />
58871              * <ul style="padding:5px;padding-left:16px;">
58872          * <li>editor - This editor</li>
58873              * <li>grid - This grid</li>
58874              * <li>record - The record being edited</li>
58875              * <li>field - The field name being edited</li>
58876              * <li>value - The value being set</li>
58877              * <li>originalValue - The original value for the field, before the edit.</li>
58878              * <li>row - The grid row index</li>
58879              * <li>column - The grid column index</li>
58880              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58881              * </ul>
58882              * @param {Object} e An edit event (see above for description)
58883              */
58884             "validateedit" : true
58885         });
58886     this.on("bodyscroll", this.stopEditing,  this);
58887     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58888 };
58889
58890 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58891     /**
58892      * @cfg {Number} clicksToEdit
58893      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58894      */
58895     clicksToEdit: 2,
58896
58897     // private
58898     isEditor : true,
58899     // private
58900     trackMouseOver: false, // causes very odd FF errors
58901
58902     onCellDblClick : function(g, row, col){
58903         this.startEditing(row, col);
58904     },
58905
58906     onEditComplete : function(ed, value, startValue){
58907         this.editing = false;
58908         this.activeEditor = null;
58909         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58910         var r = ed.record;
58911         var field = this.colModel.getDataIndex(ed.col);
58912         var e = {
58913             grid: this,
58914             record: r,
58915             field: field,
58916             originalValue: startValue,
58917             value: value,
58918             row: ed.row,
58919             column: ed.col,
58920             cancel:false,
58921             editor: ed
58922         };
58923         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58924         cell.show();
58925           
58926         if(String(value) !== String(startValue)){
58927             
58928             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58929                 r.set(field, e.value);
58930                 // if we are dealing with a combo box..
58931                 // then we also set the 'name' colum to be the displayField
58932                 if (ed.field.displayField && ed.field.name) {
58933                     r.set(ed.field.name, ed.field.el.dom.value);
58934                 }
58935                 
58936                 delete e.cancel; //?? why!!!
58937                 this.fireEvent("afteredit", e);
58938             }
58939         } else {
58940             this.fireEvent("afteredit", e); // always fire it!
58941         }
58942         this.view.focusCell(ed.row, ed.col);
58943     },
58944
58945     /**
58946      * Starts editing the specified for the specified row/column
58947      * @param {Number} rowIndex
58948      * @param {Number} colIndex
58949      */
58950     startEditing : function(row, col){
58951         this.stopEditing();
58952         if(this.colModel.isCellEditable(col, row)){
58953             this.view.ensureVisible(row, col, true);
58954           
58955             var r = this.dataSource.getAt(row);
58956             var field = this.colModel.getDataIndex(col);
58957             var cell = Roo.get(this.view.getCell(row,col));
58958             var e = {
58959                 grid: this,
58960                 record: r,
58961                 field: field,
58962                 value: r.data[field],
58963                 row: row,
58964                 column: col,
58965                 cancel:false 
58966             };
58967             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58968                 this.editing = true;
58969                 var ed = this.colModel.getCellEditor(col, row);
58970                 
58971                 if (!ed) {
58972                     return;
58973                 }
58974                 if(!ed.rendered){
58975                     ed.render(ed.parentEl || document.body);
58976                 }
58977                 ed.field.reset();
58978                
58979                 cell.hide();
58980                 
58981                 (function(){ // complex but required for focus issues in safari, ie and opera
58982                     ed.row = row;
58983                     ed.col = col;
58984                     ed.record = r;
58985                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58986                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58987                     this.activeEditor = ed;
58988                     var v = r.data[field];
58989                     ed.startEdit(this.view.getCell(row, col), v);
58990                     // combo's with 'displayField and name set
58991                     if (ed.field.displayField && ed.field.name) {
58992                         ed.field.el.dom.value = r.data[ed.field.name];
58993                     }
58994                     
58995                     
58996                 }).defer(50, this);
58997             }
58998         }
58999     },
59000         
59001     /**
59002      * Stops any active editing
59003      */
59004     stopEditing : function(){
59005         if(this.activeEditor){
59006             this.activeEditor.completeEdit();
59007         }
59008         this.activeEditor = null;
59009     },
59010         
59011          /**
59012      * Called to get grid's drag proxy text, by default returns this.ddText.
59013      * @return {String}
59014      */
59015     getDragDropText : function(){
59016         var count = this.selModel.getSelectedCell() ? 1 : 0;
59017         return String.format(this.ddText, count, count == 1 ? '' : 's');
59018     }
59019         
59020 });/*
59021  * Based on:
59022  * Ext JS Library 1.1.1
59023  * Copyright(c) 2006-2007, Ext JS, LLC.
59024  *
59025  * Originally Released Under LGPL - original licence link has changed is not relivant.
59026  *
59027  * Fork - LGPL
59028  * <script type="text/javascript">
59029  */
59030
59031 // private - not really -- you end up using it !
59032 // This is a support class used internally by the Grid components
59033
59034 /**
59035  * @class Roo.grid.GridEditor
59036  * @extends Roo.Editor
59037  * Class for creating and editable grid elements.
59038  * @param {Object} config any settings (must include field)
59039  */
59040 Roo.grid.GridEditor = function(field, config){
59041     if (!config && field.field) {
59042         config = field;
59043         field = Roo.factory(config.field, Roo.form);
59044     }
59045     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59046     field.monitorTab = false;
59047 };
59048
59049 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59050     
59051     /**
59052      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59053      */
59054     
59055     alignment: "tl-tl",
59056     autoSize: "width",
59057     hideEl : false,
59058     cls: "x-small-editor x-grid-editor",
59059     shim:false,
59060     shadow:"frame"
59061 });/*
59062  * Based on:
59063  * Ext JS Library 1.1.1
59064  * Copyright(c) 2006-2007, Ext JS, LLC.
59065  *
59066  * Originally Released Under LGPL - original licence link has changed is not relivant.
59067  *
59068  * Fork - LGPL
59069  * <script type="text/javascript">
59070  */
59071   
59072
59073   
59074 Roo.grid.PropertyRecord = Roo.data.Record.create([
59075     {name:'name',type:'string'},  'value'
59076 ]);
59077
59078
59079 Roo.grid.PropertyStore = function(grid, source){
59080     this.grid = grid;
59081     this.store = new Roo.data.Store({
59082         recordType : Roo.grid.PropertyRecord
59083     });
59084     this.store.on('update', this.onUpdate,  this);
59085     if(source){
59086         this.setSource(source);
59087     }
59088     Roo.grid.PropertyStore.superclass.constructor.call(this);
59089 };
59090
59091
59092
59093 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59094     setSource : function(o){
59095         this.source = o;
59096         this.store.removeAll();
59097         var data = [];
59098         for(var k in o){
59099             if(this.isEditableValue(o[k])){
59100                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59101             }
59102         }
59103         this.store.loadRecords({records: data}, {}, true);
59104     },
59105
59106     onUpdate : function(ds, record, type){
59107         if(type == Roo.data.Record.EDIT){
59108             var v = record.data['value'];
59109             var oldValue = record.modified['value'];
59110             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59111                 this.source[record.id] = v;
59112                 record.commit();
59113                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59114             }else{
59115                 record.reject();
59116             }
59117         }
59118     },
59119
59120     getProperty : function(row){
59121        return this.store.getAt(row);
59122     },
59123
59124     isEditableValue: function(val){
59125         if(val && val instanceof Date){
59126             return true;
59127         }else if(typeof val == 'object' || typeof val == 'function'){
59128             return false;
59129         }
59130         return true;
59131     },
59132
59133     setValue : function(prop, value){
59134         this.source[prop] = value;
59135         this.store.getById(prop).set('value', value);
59136     },
59137
59138     getSource : function(){
59139         return this.source;
59140     }
59141 });
59142
59143 Roo.grid.PropertyColumnModel = function(grid, store){
59144     this.grid = grid;
59145     var g = Roo.grid;
59146     g.PropertyColumnModel.superclass.constructor.call(this, [
59147         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59148         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59149     ]);
59150     this.store = store;
59151     this.bselect = Roo.DomHelper.append(document.body, {
59152         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59153             {tag: 'option', value: 'true', html: 'true'},
59154             {tag: 'option', value: 'false', html: 'false'}
59155         ]
59156     });
59157     Roo.id(this.bselect);
59158     var f = Roo.form;
59159     this.editors = {
59160         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59161         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59162         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59163         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59164         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59165     };
59166     this.renderCellDelegate = this.renderCell.createDelegate(this);
59167     this.renderPropDelegate = this.renderProp.createDelegate(this);
59168 };
59169
59170 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59171     
59172     
59173     nameText : 'Name',
59174     valueText : 'Value',
59175     
59176     dateFormat : 'm/j/Y',
59177     
59178     
59179     renderDate : function(dateVal){
59180         return dateVal.dateFormat(this.dateFormat);
59181     },
59182
59183     renderBool : function(bVal){
59184         return bVal ? 'true' : 'false';
59185     },
59186
59187     isCellEditable : function(colIndex, rowIndex){
59188         return colIndex == 1;
59189     },
59190
59191     getRenderer : function(col){
59192         return col == 1 ?
59193             this.renderCellDelegate : this.renderPropDelegate;
59194     },
59195
59196     renderProp : function(v){
59197         return this.getPropertyName(v);
59198     },
59199
59200     renderCell : function(val){
59201         var rv = val;
59202         if(val instanceof Date){
59203             rv = this.renderDate(val);
59204         }else if(typeof val == 'boolean'){
59205             rv = this.renderBool(val);
59206         }
59207         return Roo.util.Format.htmlEncode(rv);
59208     },
59209
59210     getPropertyName : function(name){
59211         var pn = this.grid.propertyNames;
59212         return pn && pn[name] ? pn[name] : name;
59213     },
59214
59215     getCellEditor : function(colIndex, rowIndex){
59216         var p = this.store.getProperty(rowIndex);
59217         var n = p.data['name'], val = p.data['value'];
59218         
59219         if(typeof(this.grid.customEditors[n]) == 'string'){
59220             return this.editors[this.grid.customEditors[n]];
59221         }
59222         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59223             return this.grid.customEditors[n];
59224         }
59225         if(val instanceof Date){
59226             return this.editors['date'];
59227         }else if(typeof val == 'number'){
59228             return this.editors['number'];
59229         }else if(typeof val == 'boolean'){
59230             return this.editors['boolean'];
59231         }else{
59232             return this.editors['string'];
59233         }
59234     }
59235 });
59236
59237 /**
59238  * @class Roo.grid.PropertyGrid
59239  * @extends Roo.grid.EditorGrid
59240  * This class represents the  interface of a component based property grid control.
59241  * <br><br>Usage:<pre><code>
59242  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59243       
59244  });
59245  // set any options
59246  grid.render();
59247  * </code></pre>
59248   
59249  * @constructor
59250  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59251  * The container MUST have some type of size defined for the grid to fill. The container will be
59252  * automatically set to position relative if it isn't already.
59253  * @param {Object} config A config object that sets properties on this grid.
59254  */
59255 Roo.grid.PropertyGrid = function(container, config){
59256     config = config || {};
59257     var store = new Roo.grid.PropertyStore(this);
59258     this.store = store;
59259     var cm = new Roo.grid.PropertyColumnModel(this, store);
59260     store.store.sort('name', 'ASC');
59261     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59262         ds: store.store,
59263         cm: cm,
59264         enableColLock:false,
59265         enableColumnMove:false,
59266         stripeRows:false,
59267         trackMouseOver: false,
59268         clicksToEdit:1
59269     }, config));
59270     this.getGridEl().addClass('x-props-grid');
59271     this.lastEditRow = null;
59272     this.on('columnresize', this.onColumnResize, this);
59273     this.addEvents({
59274          /**
59275              * @event beforepropertychange
59276              * Fires before a property changes (return false to stop?)
59277              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59278              * @param {String} id Record Id
59279              * @param {String} newval New Value
59280          * @param {String} oldval Old Value
59281              */
59282         "beforepropertychange": true,
59283         /**
59284              * @event propertychange
59285              * Fires after a property changes
59286              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59287              * @param {String} id Record Id
59288              * @param {String} newval New Value
59289          * @param {String} oldval Old Value
59290              */
59291         "propertychange": true
59292     });
59293     this.customEditors = this.customEditors || {};
59294 };
59295 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59296     
59297      /**
59298      * @cfg {Object} customEditors map of colnames=> custom editors.
59299      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59300      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59301      * false disables editing of the field.
59302          */
59303     
59304       /**
59305      * @cfg {Object} propertyNames map of property Names to their displayed value
59306          */
59307     
59308     render : function(){
59309         Roo.grid.PropertyGrid.superclass.render.call(this);
59310         this.autoSize.defer(100, this);
59311     },
59312
59313     autoSize : function(){
59314         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59315         if(this.view){
59316             this.view.fitColumns();
59317         }
59318     },
59319
59320     onColumnResize : function(){
59321         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59322         this.autoSize();
59323     },
59324     /**
59325      * Sets the data for the Grid
59326      * accepts a Key => Value object of all the elements avaiable.
59327      * @param {Object} data  to appear in grid.
59328      */
59329     setSource : function(source){
59330         this.store.setSource(source);
59331         //this.autoSize();
59332     },
59333     /**
59334      * Gets all the data from the grid.
59335      * @return {Object} data  data stored in grid
59336      */
59337     getSource : function(){
59338         return this.store.getSource();
59339     }
59340 });/*
59341   
59342  * Licence LGPL
59343  
59344  */
59345  
59346 /**
59347  * @class Roo.grid.Calendar
59348  * @extends Roo.util.Grid
59349  * This class extends the Grid to provide a calendar widget
59350  * <br><br>Usage:<pre><code>
59351  var grid = new Roo.grid.Calendar("my-container-id", {
59352      ds: myDataStore,
59353      cm: myColModel,
59354      selModel: mySelectionModel,
59355      autoSizeColumns: true,
59356      monitorWindowResize: false,
59357      trackMouseOver: true
59358      eventstore : real data store..
59359  });
59360  // set any options
59361  grid.render();
59362   
59363   * @constructor
59364  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59365  * The container MUST have some type of size defined for the grid to fill. The container will be
59366  * automatically set to position relative if it isn't already.
59367  * @param {Object} config A config object that sets properties on this grid.
59368  */
59369 Roo.grid.Calendar = function(container, config){
59370         // initialize the container
59371         this.container = Roo.get(container);
59372         this.container.update("");
59373         this.container.setStyle("overflow", "hidden");
59374     this.container.addClass('x-grid-container');
59375
59376     this.id = this.container.id;
59377
59378     Roo.apply(this, config);
59379     // check and correct shorthanded configs
59380     
59381     var rows = [];
59382     var d =1;
59383     for (var r = 0;r < 6;r++) {
59384         
59385         rows[r]=[];
59386         for (var c =0;c < 7;c++) {
59387             rows[r][c]= '';
59388         }
59389     }
59390     if (this.eventStore) {
59391         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59392         this.eventStore.on('load',this.onLoad, this);
59393         this.eventStore.on('beforeload',this.clearEvents, this);
59394          
59395     }
59396     
59397     this.dataSource = new Roo.data.Store({
59398             proxy: new Roo.data.MemoryProxy(rows),
59399             reader: new Roo.data.ArrayReader({}, [
59400                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59401     });
59402
59403     this.dataSource.load();
59404     this.ds = this.dataSource;
59405     this.ds.xmodule = this.xmodule || false;
59406     
59407     
59408     var cellRender = function(v,x,r)
59409     {
59410         return String.format(
59411             '<div class="fc-day  fc-widget-content"><div>' +
59412                 '<div class="fc-event-container"></div>' +
59413                 '<div class="fc-day-number">{0}</div>'+
59414                 
59415                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59416             '</div></div>', v);
59417     
59418     }
59419     
59420     
59421     this.colModel = new Roo.grid.ColumnModel( [
59422         {
59423             xtype: 'ColumnModel',
59424             xns: Roo.grid,
59425             dataIndex : 'weekday0',
59426             header : 'Sunday',
59427             renderer : cellRender
59428         },
59429         {
59430             xtype: 'ColumnModel',
59431             xns: Roo.grid,
59432             dataIndex : 'weekday1',
59433             header : 'Monday',
59434             renderer : cellRender
59435         },
59436         {
59437             xtype: 'ColumnModel',
59438             xns: Roo.grid,
59439             dataIndex : 'weekday2',
59440             header : 'Tuesday',
59441             renderer : cellRender
59442         },
59443         {
59444             xtype: 'ColumnModel',
59445             xns: Roo.grid,
59446             dataIndex : 'weekday3',
59447             header : 'Wednesday',
59448             renderer : cellRender
59449         },
59450         {
59451             xtype: 'ColumnModel',
59452             xns: Roo.grid,
59453             dataIndex : 'weekday4',
59454             header : 'Thursday',
59455             renderer : cellRender
59456         },
59457         {
59458             xtype: 'ColumnModel',
59459             xns: Roo.grid,
59460             dataIndex : 'weekday5',
59461             header : 'Friday',
59462             renderer : cellRender
59463         },
59464         {
59465             xtype: 'ColumnModel',
59466             xns: Roo.grid,
59467             dataIndex : 'weekday6',
59468             header : 'Saturday',
59469             renderer : cellRender
59470         }
59471     ]);
59472     this.cm = this.colModel;
59473     this.cm.xmodule = this.xmodule || false;
59474  
59475         
59476           
59477     //this.selModel = new Roo.grid.CellSelectionModel();
59478     //this.sm = this.selModel;
59479     //this.selModel.init(this);
59480     
59481     
59482     if(this.width){
59483         this.container.setWidth(this.width);
59484     }
59485
59486     if(this.height){
59487         this.container.setHeight(this.height);
59488     }
59489     /** @private */
59490         this.addEvents({
59491         // raw events
59492         /**
59493          * @event click
59494          * The raw click event for the entire grid.
59495          * @param {Roo.EventObject} e
59496          */
59497         "click" : true,
59498         /**
59499          * @event dblclick
59500          * The raw dblclick event for the entire grid.
59501          * @param {Roo.EventObject} e
59502          */
59503         "dblclick" : true,
59504         /**
59505          * @event contextmenu
59506          * The raw contextmenu event for the entire grid.
59507          * @param {Roo.EventObject} e
59508          */
59509         "contextmenu" : true,
59510         /**
59511          * @event mousedown
59512          * The raw mousedown event for the entire grid.
59513          * @param {Roo.EventObject} e
59514          */
59515         "mousedown" : true,
59516         /**
59517          * @event mouseup
59518          * The raw mouseup event for the entire grid.
59519          * @param {Roo.EventObject} e
59520          */
59521         "mouseup" : true,
59522         /**
59523          * @event mouseover
59524          * The raw mouseover event for the entire grid.
59525          * @param {Roo.EventObject} e
59526          */
59527         "mouseover" : true,
59528         /**
59529          * @event mouseout
59530          * The raw mouseout event for the entire grid.
59531          * @param {Roo.EventObject} e
59532          */
59533         "mouseout" : true,
59534         /**
59535          * @event keypress
59536          * The raw keypress event for the entire grid.
59537          * @param {Roo.EventObject} e
59538          */
59539         "keypress" : true,
59540         /**
59541          * @event keydown
59542          * The raw keydown event for the entire grid.
59543          * @param {Roo.EventObject} e
59544          */
59545         "keydown" : true,
59546
59547         // custom events
59548
59549         /**
59550          * @event cellclick
59551          * Fires when a cell is clicked
59552          * @param {Grid} this
59553          * @param {Number} rowIndex
59554          * @param {Number} columnIndex
59555          * @param {Roo.EventObject} e
59556          */
59557         "cellclick" : true,
59558         /**
59559          * @event celldblclick
59560          * Fires when a cell is double clicked
59561          * @param {Grid} this
59562          * @param {Number} rowIndex
59563          * @param {Number} columnIndex
59564          * @param {Roo.EventObject} e
59565          */
59566         "celldblclick" : true,
59567         /**
59568          * @event rowclick
59569          * Fires when a row is clicked
59570          * @param {Grid} this
59571          * @param {Number} rowIndex
59572          * @param {Roo.EventObject} e
59573          */
59574         "rowclick" : true,
59575         /**
59576          * @event rowdblclick
59577          * Fires when a row is double clicked
59578          * @param {Grid} this
59579          * @param {Number} rowIndex
59580          * @param {Roo.EventObject} e
59581          */
59582         "rowdblclick" : true,
59583         /**
59584          * @event headerclick
59585          * Fires when a header is clicked
59586          * @param {Grid} this
59587          * @param {Number} columnIndex
59588          * @param {Roo.EventObject} e
59589          */
59590         "headerclick" : true,
59591         /**
59592          * @event headerdblclick
59593          * Fires when a header cell is double clicked
59594          * @param {Grid} this
59595          * @param {Number} columnIndex
59596          * @param {Roo.EventObject} e
59597          */
59598         "headerdblclick" : true,
59599         /**
59600          * @event rowcontextmenu
59601          * Fires when a row is right clicked
59602          * @param {Grid} this
59603          * @param {Number} rowIndex
59604          * @param {Roo.EventObject} e
59605          */
59606         "rowcontextmenu" : true,
59607         /**
59608          * @event cellcontextmenu
59609          * Fires when a cell is right clicked
59610          * @param {Grid} this
59611          * @param {Number} rowIndex
59612          * @param {Number} cellIndex
59613          * @param {Roo.EventObject} e
59614          */
59615          "cellcontextmenu" : true,
59616         /**
59617          * @event headercontextmenu
59618          * Fires when a header is right clicked
59619          * @param {Grid} this
59620          * @param {Number} columnIndex
59621          * @param {Roo.EventObject} e
59622          */
59623         "headercontextmenu" : true,
59624         /**
59625          * @event bodyscroll
59626          * Fires when the body element is scrolled
59627          * @param {Number} scrollLeft
59628          * @param {Number} scrollTop
59629          */
59630         "bodyscroll" : true,
59631         /**
59632          * @event columnresize
59633          * Fires when the user resizes a column
59634          * @param {Number} columnIndex
59635          * @param {Number} newSize
59636          */
59637         "columnresize" : true,
59638         /**
59639          * @event columnmove
59640          * Fires when the user moves a column
59641          * @param {Number} oldIndex
59642          * @param {Number} newIndex
59643          */
59644         "columnmove" : true,
59645         /**
59646          * @event startdrag
59647          * Fires when row(s) start being dragged
59648          * @param {Grid} this
59649          * @param {Roo.GridDD} dd The drag drop object
59650          * @param {event} e The raw browser event
59651          */
59652         "startdrag" : true,
59653         /**
59654          * @event enddrag
59655          * Fires when a drag operation is complete
59656          * @param {Grid} this
59657          * @param {Roo.GridDD} dd The drag drop object
59658          * @param {event} e The raw browser event
59659          */
59660         "enddrag" : true,
59661         /**
59662          * @event dragdrop
59663          * Fires when dragged row(s) are dropped on a valid DD target
59664          * @param {Grid} this
59665          * @param {Roo.GridDD} dd The drag drop object
59666          * @param {String} targetId The target drag drop object
59667          * @param {event} e The raw browser event
59668          */
59669         "dragdrop" : true,
59670         /**
59671          * @event dragover
59672          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59673          * @param {Grid} this
59674          * @param {Roo.GridDD} dd The drag drop object
59675          * @param {String} targetId The target drag drop object
59676          * @param {event} e The raw browser event
59677          */
59678         "dragover" : true,
59679         /**
59680          * @event dragenter
59681          *  Fires when the dragged row(s) first cross another DD target while being dragged
59682          * @param {Grid} this
59683          * @param {Roo.GridDD} dd The drag drop object
59684          * @param {String} targetId The target drag drop object
59685          * @param {event} e The raw browser event
59686          */
59687         "dragenter" : true,
59688         /**
59689          * @event dragout
59690          * Fires when the dragged row(s) leave another DD target while being dragged
59691          * @param {Grid} this
59692          * @param {Roo.GridDD} dd The drag drop object
59693          * @param {String} targetId The target drag drop object
59694          * @param {event} e The raw browser event
59695          */
59696         "dragout" : true,
59697         /**
59698          * @event rowclass
59699          * Fires when a row is rendered, so you can change add a style to it.
59700          * @param {GridView} gridview   The grid view
59701          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59702          */
59703         'rowclass' : true,
59704
59705         /**
59706          * @event render
59707          * Fires when the grid is rendered
59708          * @param {Grid} grid
59709          */
59710         'render' : true,
59711             /**
59712              * @event select
59713              * Fires when a date is selected
59714              * @param {DatePicker} this
59715              * @param {Date} date The selected date
59716              */
59717         'select': true,
59718         /**
59719              * @event monthchange
59720              * Fires when the displayed month changes 
59721              * @param {DatePicker} this
59722              * @param {Date} date The selected month
59723              */
59724         'monthchange': true,
59725         /**
59726              * @event evententer
59727              * Fires when mouse over an event
59728              * @param {Calendar} this
59729              * @param {event} Event
59730              */
59731         'evententer': true,
59732         /**
59733              * @event eventleave
59734              * Fires when the mouse leaves an
59735              * @param {Calendar} this
59736              * @param {event}
59737              */
59738         'eventleave': true,
59739         /**
59740              * @event eventclick
59741              * Fires when the mouse click an
59742              * @param {Calendar} this
59743              * @param {event}
59744              */
59745         'eventclick': true,
59746         /**
59747              * @event eventrender
59748              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59749              * @param {Calendar} this
59750              * @param {data} data to be modified
59751              */
59752         'eventrender': true
59753         
59754     });
59755
59756     Roo.grid.Grid.superclass.constructor.call(this);
59757     this.on('render', function() {
59758         this.view.el.addClass('x-grid-cal'); 
59759         
59760         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59761
59762     },this);
59763     
59764     if (!Roo.grid.Calendar.style) {
59765         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59766             
59767             
59768             '.x-grid-cal .x-grid-col' :  {
59769                 height: 'auto !important',
59770                 'vertical-align': 'top'
59771             },
59772             '.x-grid-cal  .fc-event-hori' : {
59773                 height: '14px'
59774             }
59775              
59776             
59777         }, Roo.id());
59778     }
59779
59780     
59781     
59782 };
59783 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59784     /**
59785      * @cfg {Store} eventStore The store that loads events.
59786      */
59787     eventStore : 25,
59788
59789      
59790     activeDate : false,
59791     startDay : 0,
59792     autoWidth : true,
59793     monitorWindowResize : false,
59794
59795     
59796     resizeColumns : function() {
59797         var col = (this.view.el.getWidth() / 7) - 3;
59798         // loop through cols, and setWidth
59799         for(var i =0 ; i < 7 ; i++){
59800             this.cm.setColumnWidth(i, col);
59801         }
59802     },
59803      setDate :function(date) {
59804         
59805         Roo.log('setDate?');
59806         
59807         this.resizeColumns();
59808         var vd = this.activeDate;
59809         this.activeDate = date;
59810 //        if(vd && this.el){
59811 //            var t = date.getTime();
59812 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59813 //                Roo.log('using add remove');
59814 //                
59815 //                this.fireEvent('monthchange', this, date);
59816 //                
59817 //                this.cells.removeClass("fc-state-highlight");
59818 //                this.cells.each(function(c){
59819 //                   if(c.dateValue == t){
59820 //                       c.addClass("fc-state-highlight");
59821 //                       setTimeout(function(){
59822 //                            try{c.dom.firstChild.focus();}catch(e){}
59823 //                       }, 50);
59824 //                       return false;
59825 //                   }
59826 //                   return true;
59827 //                });
59828 //                return;
59829 //            }
59830 //        }
59831         
59832         var days = date.getDaysInMonth();
59833         
59834         var firstOfMonth = date.getFirstDateOfMonth();
59835         var startingPos = firstOfMonth.getDay()-this.startDay;
59836         
59837         if(startingPos < this.startDay){
59838             startingPos += 7;
59839         }
59840         
59841         var pm = date.add(Date.MONTH, -1);
59842         var prevStart = pm.getDaysInMonth()-startingPos;
59843 //        
59844         
59845         
59846         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59847         
59848         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59849         //this.cells.addClassOnOver('fc-state-hover');
59850         
59851         var cells = this.cells.elements;
59852         var textEls = this.textNodes;
59853         
59854         //Roo.each(cells, function(cell){
59855         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59856         //});
59857         
59858         days += startingPos;
59859
59860         // convert everything to numbers so it's fast
59861         var day = 86400000;
59862         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59863         //Roo.log(d);
59864         //Roo.log(pm);
59865         //Roo.log(prevStart);
59866         
59867         var today = new Date().clearTime().getTime();
59868         var sel = date.clearTime().getTime();
59869         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59870         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59871         var ddMatch = this.disabledDatesRE;
59872         var ddText = this.disabledDatesText;
59873         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59874         var ddaysText = this.disabledDaysText;
59875         var format = this.format;
59876         
59877         var setCellClass = function(cal, cell){
59878             
59879             //Roo.log('set Cell Class');
59880             cell.title = "";
59881             var t = d.getTime();
59882             
59883             //Roo.log(d);
59884             
59885             
59886             cell.dateValue = t;
59887             if(t == today){
59888                 cell.className += " fc-today";
59889                 cell.className += " fc-state-highlight";
59890                 cell.title = cal.todayText;
59891             }
59892             if(t == sel){
59893                 // disable highlight in other month..
59894                 cell.className += " fc-state-highlight";
59895                 
59896             }
59897             // disabling
59898             if(t < min) {
59899                 //cell.className = " fc-state-disabled";
59900                 cell.title = cal.minText;
59901                 return;
59902             }
59903             if(t > max) {
59904                 //cell.className = " fc-state-disabled";
59905                 cell.title = cal.maxText;
59906                 return;
59907             }
59908             if(ddays){
59909                 if(ddays.indexOf(d.getDay()) != -1){
59910                     // cell.title = ddaysText;
59911                    // cell.className = " fc-state-disabled";
59912                 }
59913             }
59914             if(ddMatch && format){
59915                 var fvalue = d.dateFormat(format);
59916                 if(ddMatch.test(fvalue)){
59917                     cell.title = ddText.replace("%0", fvalue);
59918                    cell.className = " fc-state-disabled";
59919                 }
59920             }
59921             
59922             if (!cell.initialClassName) {
59923                 cell.initialClassName = cell.dom.className;
59924             }
59925             
59926             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59927         };
59928
59929         var i = 0;
59930         
59931         for(; i < startingPos; i++) {
59932             cells[i].dayName =  (++prevStart);
59933             Roo.log(textEls[i]);
59934             d.setDate(d.getDate()+1);
59935             
59936             //cells[i].className = "fc-past fc-other-month";
59937             setCellClass(this, cells[i]);
59938         }
59939         
59940         var intDay = 0;
59941         
59942         for(; i < days; i++){
59943             intDay = i - startingPos + 1;
59944             cells[i].dayName =  (intDay);
59945             d.setDate(d.getDate()+1);
59946             
59947             cells[i].className = ''; // "x-date-active";
59948             setCellClass(this, cells[i]);
59949         }
59950         var extraDays = 0;
59951         
59952         for(; i < 42; i++) {
59953             //textEls[i].innerHTML = (++extraDays);
59954             
59955             d.setDate(d.getDate()+1);
59956             cells[i].dayName = (++extraDays);
59957             cells[i].className = "fc-future fc-other-month";
59958             setCellClass(this, cells[i]);
59959         }
59960         
59961         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59962         
59963         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59964         
59965         // this will cause all the cells to mis
59966         var rows= [];
59967         var i =0;
59968         for (var r = 0;r < 6;r++) {
59969             for (var c =0;c < 7;c++) {
59970                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59971             }    
59972         }
59973         
59974         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59975         for(i=0;i<cells.length;i++) {
59976             
59977             this.cells.elements[i].dayName = cells[i].dayName ;
59978             this.cells.elements[i].className = cells[i].className;
59979             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59980             this.cells.elements[i].title = cells[i].title ;
59981             this.cells.elements[i].dateValue = cells[i].dateValue ;
59982         }
59983         
59984         
59985         
59986         
59987         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59988         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59989         
59990         ////if(totalRows != 6){
59991             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59992            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59993        // }
59994         
59995         this.fireEvent('monthchange', this, date);
59996         
59997         
59998     },
59999  /**
60000      * Returns the grid's SelectionModel.
60001      * @return {SelectionModel}
60002      */
60003     getSelectionModel : function(){
60004         if(!this.selModel){
60005             this.selModel = new Roo.grid.CellSelectionModel();
60006         }
60007         return this.selModel;
60008     },
60009
60010     load: function() {
60011         this.eventStore.load()
60012         
60013         
60014         
60015     },
60016     
60017     findCell : function(dt) {
60018         dt = dt.clearTime().getTime();
60019         var ret = false;
60020         this.cells.each(function(c){
60021             //Roo.log("check " +c.dateValue + '?=' + dt);
60022             if(c.dateValue == dt){
60023                 ret = c;
60024                 return false;
60025             }
60026             return true;
60027         });
60028         
60029         return ret;
60030     },
60031     
60032     findCells : function(rec) {
60033         var s = rec.data.start_dt.clone().clearTime().getTime();
60034        // Roo.log(s);
60035         var e= rec.data.end_dt.clone().clearTime().getTime();
60036        // Roo.log(e);
60037         var ret = [];
60038         this.cells.each(function(c){
60039              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60040             
60041             if(c.dateValue > e){
60042                 return ;
60043             }
60044             if(c.dateValue < s){
60045                 return ;
60046             }
60047             ret.push(c);
60048         });
60049         
60050         return ret;    
60051     },
60052     
60053     findBestRow: function(cells)
60054     {
60055         var ret = 0;
60056         
60057         for (var i =0 ; i < cells.length;i++) {
60058             ret  = Math.max(cells[i].rows || 0,ret);
60059         }
60060         return ret;
60061         
60062     },
60063     
60064     
60065     addItem : function(rec)
60066     {
60067         // look for vertical location slot in
60068         var cells = this.findCells(rec);
60069         
60070         rec.row = this.findBestRow(cells);
60071         
60072         // work out the location.
60073         
60074         var crow = false;
60075         var rows = [];
60076         for(var i =0; i < cells.length; i++) {
60077             if (!crow) {
60078                 crow = {
60079                     start : cells[i],
60080                     end :  cells[i]
60081                 };
60082                 continue;
60083             }
60084             if (crow.start.getY() == cells[i].getY()) {
60085                 // on same row.
60086                 crow.end = cells[i];
60087                 continue;
60088             }
60089             // different row.
60090             rows.push(crow);
60091             crow = {
60092                 start: cells[i],
60093                 end : cells[i]
60094             };
60095             
60096         }
60097         
60098         rows.push(crow);
60099         rec.els = [];
60100         rec.rows = rows;
60101         rec.cells = cells;
60102         for (var i = 0; i < cells.length;i++) {
60103             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60104             
60105         }
60106         
60107         
60108     },
60109     
60110     clearEvents: function() {
60111         
60112         if (!this.eventStore.getCount()) {
60113             return;
60114         }
60115         // reset number of rows in cells.
60116         Roo.each(this.cells.elements, function(c){
60117             c.rows = 0;
60118         });
60119         
60120         this.eventStore.each(function(e) {
60121             this.clearEvent(e);
60122         },this);
60123         
60124     },
60125     
60126     clearEvent : function(ev)
60127     {
60128         if (ev.els) {
60129             Roo.each(ev.els, function(el) {
60130                 el.un('mouseenter' ,this.onEventEnter, this);
60131                 el.un('mouseleave' ,this.onEventLeave, this);
60132                 el.remove();
60133             },this);
60134             ev.els = [];
60135         }
60136     },
60137     
60138     
60139     renderEvent : function(ev,ctr) {
60140         if (!ctr) {
60141              ctr = this.view.el.select('.fc-event-container',true).first();
60142         }
60143         
60144          
60145         this.clearEvent(ev);
60146             //code
60147        
60148         
60149         
60150         ev.els = [];
60151         var cells = ev.cells;
60152         var rows = ev.rows;
60153         this.fireEvent('eventrender', this, ev);
60154         
60155         for(var i =0; i < rows.length; i++) {
60156             
60157             cls = '';
60158             if (i == 0) {
60159                 cls += ' fc-event-start';
60160             }
60161             if ((i+1) == rows.length) {
60162                 cls += ' fc-event-end';
60163             }
60164             
60165             //Roo.log(ev.data);
60166             // how many rows should it span..
60167             var cg = this.eventTmpl.append(ctr,Roo.apply({
60168                 fccls : cls
60169                 
60170             }, ev.data) , true);
60171             
60172             
60173             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60174             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60175             cg.on('click', this.onEventClick, this, ev);
60176             
60177             ev.els.push(cg);
60178             
60179             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60180             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60181             //Roo.log(cg);
60182              
60183             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60184             cg.setWidth(ebox.right - sbox.x -2);
60185         }
60186     },
60187     
60188     renderEvents: function()
60189     {   
60190         // first make sure there is enough space..
60191         
60192         if (!this.eventTmpl) {
60193             this.eventTmpl = new Roo.Template(
60194                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60195                     '<div class="fc-event-inner">' +
60196                         '<span class="fc-event-time">{time}</span>' +
60197                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60198                     '</div>' +
60199                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60200                 '</div>'
60201             );
60202                 
60203         }
60204                
60205         
60206         
60207         this.cells.each(function(c) {
60208             //Roo.log(c.select('.fc-day-content div',true).first());
60209             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60210         });
60211         
60212         var ctr = this.view.el.select('.fc-event-container',true).first();
60213         
60214         var cls;
60215         this.eventStore.each(function(ev){
60216             
60217             this.renderEvent(ev);
60218              
60219              
60220         }, this);
60221         this.view.layout();
60222         
60223     },
60224     
60225     onEventEnter: function (e, el,event,d) {
60226         this.fireEvent('evententer', this, el, event);
60227     },
60228     
60229     onEventLeave: function (e, el,event,d) {
60230         this.fireEvent('eventleave', this, el, event);
60231     },
60232     
60233     onEventClick: function (e, el,event,d) {
60234         this.fireEvent('eventclick', this, el, event);
60235     },
60236     
60237     onMonthChange: function () {
60238         this.store.load();
60239     },
60240     
60241     onLoad: function () {
60242         
60243         //Roo.log('calendar onload');
60244 //         
60245         if(this.eventStore.getCount() > 0){
60246             
60247            
60248             
60249             this.eventStore.each(function(d){
60250                 
60251                 
60252                 // FIXME..
60253                 var add =   d.data;
60254                 if (typeof(add.end_dt) == 'undefined')  {
60255                     Roo.log("Missing End time in calendar data: ");
60256                     Roo.log(d);
60257                     return;
60258                 }
60259                 if (typeof(add.start_dt) == 'undefined')  {
60260                     Roo.log("Missing Start time in calendar data: ");
60261                     Roo.log(d);
60262                     return;
60263                 }
60264                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60265                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60266                 add.id = add.id || d.id;
60267                 add.title = add.title || '??';
60268                 
60269                 this.addItem(d);
60270                 
60271              
60272             },this);
60273         }
60274         
60275         this.renderEvents();
60276     }
60277     
60278
60279 });
60280 /*
60281  grid : {
60282                 xtype: 'Grid',
60283                 xns: Roo.grid,
60284                 listeners : {
60285                     render : function ()
60286                     {
60287                         _this.grid = this;
60288                         
60289                         if (!this.view.el.hasClass('course-timesheet')) {
60290                             this.view.el.addClass('course-timesheet');
60291                         }
60292                         if (this.tsStyle) {
60293                             this.ds.load({});
60294                             return; 
60295                         }
60296                         Roo.log('width');
60297                         Roo.log(_this.grid.view.el.getWidth());
60298                         
60299                         
60300                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60301                             '.course-timesheet .x-grid-row' : {
60302                                 height: '80px'
60303                             },
60304                             '.x-grid-row td' : {
60305                                 'vertical-align' : 0
60306                             },
60307                             '.course-edit-link' : {
60308                                 'color' : 'blue',
60309                                 'text-overflow' : 'ellipsis',
60310                                 'overflow' : 'hidden',
60311                                 'white-space' : 'nowrap',
60312                                 'cursor' : 'pointer'
60313                             },
60314                             '.sub-link' : {
60315                                 'color' : 'green'
60316                             },
60317                             '.de-act-sup-link' : {
60318                                 'color' : 'purple',
60319                                 'text-decoration' : 'line-through'
60320                             },
60321                             '.de-act-link' : {
60322                                 'color' : 'red',
60323                                 'text-decoration' : 'line-through'
60324                             },
60325                             '.course-timesheet .course-highlight' : {
60326                                 'border-top-style': 'dashed !important',
60327                                 'border-bottom-bottom': 'dashed !important'
60328                             },
60329                             '.course-timesheet .course-item' : {
60330                                 'font-family'   : 'tahoma, arial, helvetica',
60331                                 'font-size'     : '11px',
60332                                 'overflow'      : 'hidden',
60333                                 'padding-left'  : '10px',
60334                                 'padding-right' : '10px',
60335                                 'padding-top' : '10px' 
60336                             }
60337                             
60338                         }, Roo.id());
60339                                 this.ds.load({});
60340                     }
60341                 },
60342                 autoWidth : true,
60343                 monitorWindowResize : false,
60344                 cellrenderer : function(v,x,r)
60345                 {
60346                     return v;
60347                 },
60348                 sm : {
60349                     xtype: 'CellSelectionModel',
60350                     xns: Roo.grid
60351                 },
60352                 dataSource : {
60353                     xtype: 'Store',
60354                     xns: Roo.data,
60355                     listeners : {
60356                         beforeload : function (_self, options)
60357                         {
60358                             options.params = options.params || {};
60359                             options.params._month = _this.monthField.getValue();
60360                             options.params.limit = 9999;
60361                             options.params['sort'] = 'when_dt';    
60362                             options.params['dir'] = 'ASC';    
60363                             this.proxy.loadResponse = this.loadResponse;
60364                             Roo.log("load?");
60365                             //this.addColumns();
60366                         },
60367                         load : function (_self, records, options)
60368                         {
60369                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60370                                 // if you click on the translation.. you can edit it...
60371                                 var el = Roo.get(this);
60372                                 var id = el.dom.getAttribute('data-id');
60373                                 var d = el.dom.getAttribute('data-date');
60374                                 var t = el.dom.getAttribute('data-time');
60375                                 //var id = this.child('span').dom.textContent;
60376                                 
60377                                 //Roo.log(this);
60378                                 Pman.Dialog.CourseCalendar.show({
60379                                     id : id,
60380                                     when_d : d,
60381                                     when_t : t,
60382                                     productitem_active : id ? 1 : 0
60383                                 }, function() {
60384                                     _this.grid.ds.load({});
60385                                 });
60386                            
60387                            });
60388                            
60389                            _this.panel.fireEvent('resize', [ '', '' ]);
60390                         }
60391                     },
60392                     loadResponse : function(o, success, response){
60393                             // this is overridden on before load..
60394                             
60395                             Roo.log("our code?");       
60396                             //Roo.log(success);
60397                             //Roo.log(response)
60398                             delete this.activeRequest;
60399                             if(!success){
60400                                 this.fireEvent("loadexception", this, o, response);
60401                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60402                                 return;
60403                             }
60404                             var result;
60405                             try {
60406                                 result = o.reader.read(response);
60407                             }catch(e){
60408                                 Roo.log("load exception?");
60409                                 this.fireEvent("loadexception", this, o, response, e);
60410                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60411                                 return;
60412                             }
60413                             Roo.log("ready...");        
60414                             // loop through result.records;
60415                             // and set this.tdate[date] = [] << array of records..
60416                             _this.tdata  = {};
60417                             Roo.each(result.records, function(r){
60418                                 //Roo.log(r.data);
60419                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60420                                     _this.tdata[r.data.when_dt.format('j')] = [];
60421                                 }
60422                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60423                             });
60424                             
60425                             //Roo.log(_this.tdata);
60426                             
60427                             result.records = [];
60428                             result.totalRecords = 6;
60429                     
60430                             // let's generate some duumy records for the rows.
60431                             //var st = _this.dateField.getValue();
60432                             
60433                             // work out monday..
60434                             //st = st.add(Date.DAY, -1 * st.format('w'));
60435                             
60436                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60437                             
60438                             var firstOfMonth = date.getFirstDayOfMonth();
60439                             var days = date.getDaysInMonth();
60440                             var d = 1;
60441                             var firstAdded = false;
60442                             for (var i = 0; i < result.totalRecords ; i++) {
60443                                 //var d= st.add(Date.DAY, i);
60444                                 var row = {};
60445                                 var added = 0;
60446                                 for(var w = 0 ; w < 7 ; w++){
60447                                     if(!firstAdded && firstOfMonth != w){
60448                                         continue;
60449                                     }
60450                                     if(d > days){
60451                                         continue;
60452                                     }
60453                                     firstAdded = true;
60454                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60455                                     row['weekday'+w] = String.format(
60456                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60457                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60458                                                     d,
60459                                                     date.format('Y-m-')+dd
60460                                                 );
60461                                     added++;
60462                                     if(typeof(_this.tdata[d]) != 'undefined'){
60463                                         Roo.each(_this.tdata[d], function(r){
60464                                             var is_sub = '';
60465                                             var deactive = '';
60466                                             var id = r.id;
60467                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60468                                             if(r.parent_id*1>0){
60469                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60470                                                 id = r.parent_id;
60471                                             }
60472                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60473                                                 deactive = 'de-act-link';
60474                                             }
60475                                             
60476                                             row['weekday'+w] += String.format(
60477                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60478                                                     id, //0
60479                                                     r.product_id_name, //1
60480                                                     r.when_dt.format('h:ia'), //2
60481                                                     is_sub, //3
60482                                                     deactive, //4
60483                                                     desc // 5
60484                                             );
60485                                         });
60486                                     }
60487                                     d++;
60488                                 }
60489                                 
60490                                 // only do this if something added..
60491                                 if(added > 0){ 
60492                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60493                                 }
60494                                 
60495                                 
60496                                 // push it twice. (second one with an hour..
60497                                 
60498                             }
60499                             //Roo.log(result);
60500                             this.fireEvent("load", this, o, o.request.arg);
60501                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60502                         },
60503                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60504                     proxy : {
60505                         xtype: 'HttpProxy',
60506                         xns: Roo.data,
60507                         method : 'GET',
60508                         url : baseURL + '/Roo/Shop_course.php'
60509                     },
60510                     reader : {
60511                         xtype: 'JsonReader',
60512                         xns: Roo.data,
60513                         id : 'id',
60514                         fields : [
60515                             {
60516                                 'name': 'id',
60517                                 'type': 'int'
60518                             },
60519                             {
60520                                 'name': 'when_dt',
60521                                 'type': 'string'
60522                             },
60523                             {
60524                                 'name': 'end_dt',
60525                                 'type': 'string'
60526                             },
60527                             {
60528                                 'name': 'parent_id',
60529                                 'type': 'int'
60530                             },
60531                             {
60532                                 'name': 'product_id',
60533                                 'type': 'int'
60534                             },
60535                             {
60536                                 'name': 'productitem_id',
60537                                 'type': 'int'
60538                             },
60539                             {
60540                                 'name': 'guid',
60541                                 'type': 'int'
60542                             }
60543                         ]
60544                     }
60545                 },
60546                 toolbar : {
60547                     xtype: 'Toolbar',
60548                     xns: Roo,
60549                     items : [
60550                         {
60551                             xtype: 'Button',
60552                             xns: Roo.Toolbar,
60553                             listeners : {
60554                                 click : function (_self, e)
60555                                 {
60556                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60557                                     sd.setMonth(sd.getMonth()-1);
60558                                     _this.monthField.setValue(sd.format('Y-m-d'));
60559                                     _this.grid.ds.load({});
60560                                 }
60561                             },
60562                             text : "Back"
60563                         },
60564                         {
60565                             xtype: 'Separator',
60566                             xns: Roo.Toolbar
60567                         },
60568                         {
60569                             xtype: 'MonthField',
60570                             xns: Roo.form,
60571                             listeners : {
60572                                 render : function (_self)
60573                                 {
60574                                     _this.monthField = _self;
60575                                    // _this.monthField.set  today
60576                                 },
60577                                 select : function (combo, date)
60578                                 {
60579                                     _this.grid.ds.load({});
60580                                 }
60581                             },
60582                             value : (function() { return new Date(); })()
60583                         },
60584                         {
60585                             xtype: 'Separator',
60586                             xns: Roo.Toolbar
60587                         },
60588                         {
60589                             xtype: 'TextItem',
60590                             xns: Roo.Toolbar,
60591                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60592                         },
60593                         {
60594                             xtype: 'Fill',
60595                             xns: Roo.Toolbar
60596                         },
60597                         {
60598                             xtype: 'Button',
60599                             xns: Roo.Toolbar,
60600                             listeners : {
60601                                 click : function (_self, e)
60602                                 {
60603                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60604                                     sd.setMonth(sd.getMonth()+1);
60605                                     _this.monthField.setValue(sd.format('Y-m-d'));
60606                                     _this.grid.ds.load({});
60607                                 }
60608                             },
60609                             text : "Next"
60610                         }
60611                     ]
60612                 },
60613                  
60614             }
60615         };
60616         
60617         *//*
60618  * Based on:
60619  * Ext JS Library 1.1.1
60620  * Copyright(c) 2006-2007, Ext JS, LLC.
60621  *
60622  * Originally Released Under LGPL - original licence link has changed is not relivant.
60623  *
60624  * Fork - LGPL
60625  * <script type="text/javascript">
60626  */
60627  
60628 /**
60629  * @class Roo.LoadMask
60630  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60631  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60632  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60633  * element's UpdateManager load indicator and will be destroyed after the initial load.
60634  * @constructor
60635  * Create a new LoadMask
60636  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60637  * @param {Object} config The config object
60638  */
60639 Roo.LoadMask = function(el, config){
60640     this.el = Roo.get(el);
60641     Roo.apply(this, config);
60642     if(this.store){
60643         this.store.on('beforeload', this.onBeforeLoad, this);
60644         this.store.on('load', this.onLoad, this);
60645         this.store.on('loadexception', this.onLoadException, this);
60646         this.removeMask = false;
60647     }else{
60648         var um = this.el.getUpdateManager();
60649         um.showLoadIndicator = false; // disable the default indicator
60650         um.on('beforeupdate', this.onBeforeLoad, this);
60651         um.on('update', this.onLoad, this);
60652         um.on('failure', this.onLoad, this);
60653         this.removeMask = true;
60654     }
60655 };
60656
60657 Roo.LoadMask.prototype = {
60658     /**
60659      * @cfg {Boolean} removeMask
60660      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60661      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60662      */
60663     /**
60664      * @cfg {String} msg
60665      * The text to display in a centered loading message box (defaults to 'Loading...')
60666      */
60667     msg : 'Loading...',
60668     /**
60669      * @cfg {String} msgCls
60670      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60671      */
60672     msgCls : 'x-mask-loading',
60673
60674     /**
60675      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60676      * @type Boolean
60677      */
60678     disabled: false,
60679
60680     /**
60681      * Disables the mask to prevent it from being displayed
60682      */
60683     disable : function(){
60684        this.disabled = true;
60685     },
60686
60687     /**
60688      * Enables the mask so that it can be displayed
60689      */
60690     enable : function(){
60691         this.disabled = false;
60692     },
60693     
60694     onLoadException : function()
60695     {
60696         Roo.log(arguments);
60697         
60698         if (typeof(arguments[3]) != 'undefined') {
60699             Roo.MessageBox.alert("Error loading",arguments[3]);
60700         } 
60701         /*
60702         try {
60703             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60704                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60705             }   
60706         } catch(e) {
60707             
60708         }
60709         */
60710     
60711         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60712     },
60713     // private
60714     onLoad : function()
60715     {
60716         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60717     },
60718
60719     // private
60720     onBeforeLoad : function(){
60721         if(!this.disabled){
60722             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60723         }
60724     },
60725
60726     // private
60727     destroy : function(){
60728         if(this.store){
60729             this.store.un('beforeload', this.onBeforeLoad, this);
60730             this.store.un('load', this.onLoad, this);
60731             this.store.un('loadexception', this.onLoadException, this);
60732         }else{
60733             var um = this.el.getUpdateManager();
60734             um.un('beforeupdate', this.onBeforeLoad, this);
60735             um.un('update', this.onLoad, this);
60736             um.un('failure', this.onLoad, this);
60737         }
60738     }
60739 };/*
60740  * Based on:
60741  * Ext JS Library 1.1.1
60742  * Copyright(c) 2006-2007, Ext JS, LLC.
60743  *
60744  * Originally Released Under LGPL - original licence link has changed is not relivant.
60745  *
60746  * Fork - LGPL
60747  * <script type="text/javascript">
60748  */
60749
60750
60751 /**
60752  * @class Roo.XTemplate
60753  * @extends Roo.Template
60754  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60755 <pre><code>
60756 var t = new Roo.XTemplate(
60757         '&lt;select name="{name}"&gt;',
60758                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60759         '&lt;/select&gt;'
60760 );
60761  
60762 // then append, applying the master template values
60763  </code></pre>
60764  *
60765  * Supported features:
60766  *
60767  *  Tags:
60768
60769 <pre><code>
60770       {a_variable} - output encoded.
60771       {a_variable.format:("Y-m-d")} - call a method on the variable
60772       {a_variable:raw} - unencoded output
60773       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60774       {a_variable:this.method_on_template(...)} - call a method on the template object.
60775  
60776 </code></pre>
60777  *  The tpl tag:
60778 <pre><code>
60779         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60780         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60781         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60782         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60783   
60784         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60785         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60786 </code></pre>
60787  *      
60788  */
60789 Roo.XTemplate = function()
60790 {
60791     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60792     if (this.html) {
60793         this.compile();
60794     }
60795 };
60796
60797
60798 Roo.extend(Roo.XTemplate, Roo.Template, {
60799
60800     /**
60801      * The various sub templates
60802      */
60803     tpls : false,
60804     /**
60805      *
60806      * basic tag replacing syntax
60807      * WORD:WORD()
60808      *
60809      * // you can fake an object call by doing this
60810      *  x.t:(test,tesT) 
60811      * 
60812      */
60813     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60814
60815     /**
60816      * compile the template
60817      *
60818      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60819      *
60820      */
60821     compile: function()
60822     {
60823         var s = this.html;
60824      
60825         s = ['<tpl>', s, '</tpl>'].join('');
60826     
60827         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60828             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60829             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60830             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60831             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60832             m,
60833             id     = 0,
60834             tpls   = [];
60835     
60836         while(true == !!(m = s.match(re))){
60837             var forMatch   = m[0].match(nameRe),
60838                 ifMatch   = m[0].match(ifRe),
60839                 execMatch   = m[0].match(execRe),
60840                 namedMatch   = m[0].match(namedRe),
60841                 
60842                 exp  = null, 
60843                 fn   = null,
60844                 exec = null,
60845                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60846                 
60847             if (ifMatch) {
60848                 // if - puts fn into test..
60849                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60850                 if(exp){
60851                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60852                 }
60853             }
60854             
60855             if (execMatch) {
60856                 // exec - calls a function... returns empty if true is  returned.
60857                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60858                 if(exp){
60859                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60860                 }
60861             }
60862             
60863             
60864             if (name) {
60865                 // for = 
60866                 switch(name){
60867                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60868                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60869                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60870                 }
60871             }
60872             var uid = namedMatch ? namedMatch[1] : id;
60873             
60874             
60875             tpls.push({
60876                 id:     namedMatch ? namedMatch[1] : id,
60877                 target: name,
60878                 exec:   exec,
60879                 test:   fn,
60880                 body:   m[1] || ''
60881             });
60882             if (namedMatch) {
60883                 s = s.replace(m[0], '');
60884             } else { 
60885                 s = s.replace(m[0], '{xtpl'+ id + '}');
60886             }
60887             ++id;
60888         }
60889         this.tpls = [];
60890         for(var i = tpls.length-1; i >= 0; --i){
60891             this.compileTpl(tpls[i]);
60892             this.tpls[tpls[i].id] = tpls[i];
60893         }
60894         this.master = tpls[tpls.length-1];
60895         return this;
60896     },
60897     /**
60898      * same as applyTemplate, except it's done to one of the subTemplates
60899      * when using named templates, you can do:
60900      *
60901      * var str = pl.applySubTemplate('your-name', values);
60902      *
60903      * 
60904      * @param {Number} id of the template
60905      * @param {Object} values to apply to template
60906      * @param {Object} parent (normaly the instance of this object)
60907      */
60908     applySubTemplate : function(id, values, parent)
60909     {
60910         
60911         
60912         var t = this.tpls[id];
60913         
60914         
60915         try { 
60916             if(t.test && !t.test.call(this, values, parent)){
60917                 return '';
60918             }
60919         } catch(e) {
60920             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60921             Roo.log(e.toString());
60922             Roo.log(t.test);
60923             return ''
60924         }
60925         try { 
60926             
60927             if(t.exec && t.exec.call(this, values, parent)){
60928                 return '';
60929             }
60930         } catch(e) {
60931             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60932             Roo.log(e.toString());
60933             Roo.log(t.exec);
60934             return ''
60935         }
60936         try {
60937             var vs = t.target ? t.target.call(this, values, parent) : values;
60938             parent = t.target ? values : parent;
60939             if(t.target && vs instanceof Array){
60940                 var buf = [];
60941                 for(var i = 0, len = vs.length; i < len; i++){
60942                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60943                 }
60944                 return buf.join('');
60945             }
60946             return t.compiled.call(this, vs, parent);
60947         } catch (e) {
60948             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60949             Roo.log(e.toString());
60950             Roo.log(t.compiled);
60951             return '';
60952         }
60953     },
60954
60955     compileTpl : function(tpl)
60956     {
60957         var fm = Roo.util.Format;
60958         var useF = this.disableFormats !== true;
60959         var sep = Roo.isGecko ? "+" : ",";
60960         var undef = function(str) {
60961             Roo.log("Property not found :"  + str);
60962             return '';
60963         };
60964         
60965         var fn = function(m, name, format, args)
60966         {
60967             //Roo.log(arguments);
60968             args = args ? args.replace(/\\'/g,"'") : args;
60969             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60970             if (typeof(format) == 'undefined') {
60971                 format= 'htmlEncode';
60972             }
60973             if (format == 'raw' ) {
60974                 format = false;
60975             }
60976             
60977             if(name.substr(0, 4) == 'xtpl'){
60978                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60979             }
60980             
60981             // build an array of options to determine if value is undefined..
60982             
60983             // basically get 'xxxx.yyyy' then do
60984             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60985             //    (function () { Roo.log("Property not found"); return ''; })() :
60986             //    ......
60987             
60988             var udef_ar = [];
60989             var lookfor = '';
60990             Roo.each(name.split('.'), function(st) {
60991                 lookfor += (lookfor.length ? '.': '') + st;
60992                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60993             });
60994             
60995             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60996             
60997             
60998             if(format && useF){
60999                 
61000                 args = args ? ',' + args : "";
61001                  
61002                 if(format.substr(0, 5) != "this."){
61003                     format = "fm." + format + '(';
61004                 }else{
61005                     format = 'this.call("'+ format.substr(5) + '", ';
61006                     args = ", values";
61007                 }
61008                 
61009                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61010             }
61011              
61012             if (args.length) {
61013                 // called with xxyx.yuu:(test,test)
61014                 // change to ()
61015                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61016             }
61017             // raw.. - :raw modifier..
61018             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61019             
61020         };
61021         var body;
61022         // branched to use + in gecko and [].join() in others
61023         if(Roo.isGecko){
61024             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61025                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61026                     "';};};";
61027         }else{
61028             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61029             body.push(tpl.body.replace(/(\r\n|\n)/g,
61030                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61031             body.push("'].join('');};};");
61032             body = body.join('');
61033         }
61034         
61035         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61036        
61037         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61038         eval(body);
61039         
61040         return this;
61041     },
61042
61043     applyTemplate : function(values){
61044         return this.master.compiled.call(this, values, {});
61045         //var s = this.subs;
61046     },
61047
61048     apply : function(){
61049         return this.applyTemplate.apply(this, arguments);
61050     }
61051
61052  });
61053
61054 Roo.XTemplate.from = function(el){
61055     el = Roo.getDom(el);
61056     return new Roo.XTemplate(el.value || el.innerHTML);
61057 };