roojs-ui.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                 var args = Array.prototype.slice.call(arguments, 0);
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "")
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         visibilityMode : 1,
7136         /**
7137          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7138          * @type String
7139          */
7140         defaultUnit : "px",
7141         
7142         /**
7143          * Sets the element's visibility mode. When setVisible() is called it
7144          * will use this to determine whether to set the visibility or the display property.
7145          * @param visMode Element.VISIBILITY or Element.DISPLAY
7146          * @return {Roo.Element} this
7147          */
7148         setVisibilityMode : function(visMode){
7149             this.visibilityMode = visMode;
7150             return this;
7151         },
7152         /**
7153          * Convenience method for setVisibilityMode(Element.DISPLAY)
7154          * @param {String} display (optional) What to set display to when visible
7155          * @return {Roo.Element} this
7156          */
7157         enableDisplayMode : function(display){
7158             this.setVisibilityMode(El.DISPLAY);
7159             if(typeof display != "undefined") { this.originalDisplay = display; }
7160             return this;
7161         },
7162
7163         /**
7164          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7165          * @param {String} selector The simple selector to test
7166          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7167                 search as a number or element (defaults to 10 || document.body)
7168          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7169          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7170          */
7171         findParent : function(simpleSelector, maxDepth, returnEl){
7172             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7173             maxDepth = maxDepth || 50;
7174             if(typeof maxDepth != "number"){
7175                 stopEl = Roo.getDom(maxDepth);
7176                 maxDepth = 10;
7177             }
7178             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7179                 if(dq.is(p, simpleSelector)){
7180                     return returnEl ? Roo.get(p) : p;
7181                 }
7182                 depth++;
7183                 p = p.parentNode;
7184             }
7185             return null;
7186         },
7187
7188
7189         /**
7190          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7191          * @param {String} selector The simple selector to test
7192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7193                 search as a number or element (defaults to 10 || document.body)
7194          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7195          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7196          */
7197         findParentNode : function(simpleSelector, maxDepth, returnEl){
7198             var p = Roo.fly(this.dom.parentNode, '_internal');
7199             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7200         },
7201         
7202         /**
7203          * Looks at  the scrollable parent element
7204          */
7205         findScrollableParent : function()
7206         {
7207             var overflowRegex = /(auto|scroll)/;
7208             
7209             if(this.getStyle('position') === 'fixed'){
7210                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211             }
7212             
7213             var excludeStaticParent = this.getStyle('position') === "absolute";
7214             
7215             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7216                 
7217                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7218                     continue;
7219                 }
7220                 
7221                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7222                     return parent;
7223                 }
7224                 
7225                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7226                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7227                 }
7228             }
7229             
7230             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7231         },
7232
7233         /**
7234          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7235          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7240          */
7241         up : function(simpleSelector, maxDepth){
7242             return this.findParentNode(simpleSelector, maxDepth, true);
7243         },
7244
7245
7246
7247         /**
7248          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @return {Boolean} True if this element matches the selector, else false
7251          */
7252         is : function(simpleSelector){
7253             return Roo.DomQuery.is(this.dom, simpleSelector);
7254         },
7255
7256         /**
7257          * Perform animation on this element.
7258          * @param {Object} args The YUI animation control args
7259          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7260          * @param {Function} onComplete (optional) Function to call when animation completes
7261          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7262          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7263          * @return {Roo.Element} this
7264          */
7265         animate : function(args, duration, onComplete, easing, animType){
7266             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7267             return this;
7268         },
7269
7270         /*
7271          * @private Internal animation call
7272          */
7273         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7274             animType = animType || 'run';
7275             opt = opt || {};
7276             var anim = Roo.lib.Anim[animType](
7277                 this.dom, args,
7278                 (opt.duration || defaultDur) || .35,
7279                 (opt.easing || defaultEase) || 'easeOut',
7280                 function(){
7281                     Roo.callback(cb, this);
7282                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7283                 },
7284                 this
7285             );
7286             opt.anim = anim;
7287             return anim;
7288         },
7289
7290         // private legacy anim prep
7291         preanim : function(a, i){
7292             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7293         },
7294
7295         /**
7296          * Removes worthless text nodes
7297          * @param {Boolean} forceReclean (optional) By default the element
7298          * keeps track if it has been cleaned already so
7299          * you can call this over and over. However, if you update the element and
7300          * need to force a reclean, you can pass true.
7301          */
7302         clean : function(forceReclean){
7303             if(this.isCleaned && forceReclean !== true){
7304                 return this;
7305             }
7306             var ns = /\S/;
7307             var d = this.dom, n = d.firstChild, ni = -1;
7308             while(n){
7309                 var nx = n.nextSibling;
7310                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7311                     d.removeChild(n);
7312                 }else{
7313                     n.nodeIndex = ++ni;
7314                 }
7315                 n = nx;
7316             }
7317             this.isCleaned = true;
7318             return this;
7319         },
7320
7321         // private
7322         calcOffsetsTo : function(el){
7323             el = Roo.get(el);
7324             var d = el.dom;
7325             var restorePos = false;
7326             if(el.getStyle('position') == 'static'){
7327                 el.position('relative');
7328                 restorePos = true;
7329             }
7330             var x = 0, y =0;
7331             var op = this.dom;
7332             while(op && op != d && op.tagName != 'HTML'){
7333                 x+= op.offsetLeft;
7334                 y+= op.offsetTop;
7335                 op = op.offsetParent;
7336             }
7337             if(restorePos){
7338                 el.position('static');
7339             }
7340             return [x, y];
7341         },
7342
7343         /**
7344          * Scrolls this element into view within the passed container.
7345          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7346          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7347          * @return {Roo.Element} this
7348          */
7349         scrollIntoView : function(container, hscroll){
7350             var c = Roo.getDom(container) || document.body;
7351             var el = this.dom;
7352
7353             var o = this.calcOffsetsTo(c),
7354                 l = o[0],
7355                 t = o[1],
7356                 b = t+el.offsetHeight,
7357                 r = l+el.offsetWidth;
7358
7359             var ch = c.clientHeight;
7360             var ct = parseInt(c.scrollTop, 10);
7361             var cl = parseInt(c.scrollLeft, 10);
7362             var cb = ct + ch;
7363             var cr = cl + c.clientWidth;
7364
7365             if(t < ct){
7366                 c.scrollTop = t;
7367             }else if(b > cb){
7368                 c.scrollTop = b-ch;
7369             }
7370
7371             if(hscroll !== false){
7372                 if(l < cl){
7373                     c.scrollLeft = l;
7374                 }else if(r > cr){
7375                     c.scrollLeft = r-c.clientWidth;
7376                 }
7377             }
7378             return this;
7379         },
7380
7381         // private
7382         scrollChildIntoView : function(child, hscroll){
7383             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7384         },
7385
7386         /**
7387          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7388          * the new height may not be available immediately.
7389          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7390          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7391          * @param {Function} onComplete (optional) Function to call when animation completes
7392          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7393          * @return {Roo.Element} this
7394          */
7395         autoHeight : function(animate, duration, onComplete, easing){
7396             var oldHeight = this.getHeight();
7397             this.clip();
7398             this.setHeight(1); // force clipping
7399             setTimeout(function(){
7400                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7401                 if(!animate){
7402                     this.setHeight(height);
7403                     this.unclip();
7404                     if(typeof onComplete == "function"){
7405                         onComplete();
7406                     }
7407                 }else{
7408                     this.setHeight(oldHeight); // restore original height
7409                     this.setHeight(height, animate, duration, function(){
7410                         this.unclip();
7411                         if(typeof onComplete == "function") { onComplete(); }
7412                     }.createDelegate(this), easing);
7413                 }
7414             }.createDelegate(this), 0);
7415             return this;
7416         },
7417
7418         /**
7419          * Returns true if this element is an ancestor of the passed element
7420          * @param {HTMLElement/String} el The element to check
7421          * @return {Boolean} True if this element is an ancestor of el, else false
7422          */
7423         contains : function(el){
7424             if(!el){return false;}
7425             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7426         },
7427
7428         /**
7429          * Checks whether the element is currently visible using both visibility and display properties.
7430          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7431          * @return {Boolean} True if the element is currently visible, else false
7432          */
7433         isVisible : function(deep) {
7434             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7435             if(deep !== true || !vis){
7436                 return vis;
7437             }
7438             var p = this.dom.parentNode;
7439             while(p && p.tagName.toLowerCase() != "body"){
7440                 if(!Roo.fly(p, '_isVisible').isVisible()){
7441                     return false;
7442                 }
7443                 p = p.parentNode;
7444             }
7445             return true;
7446         },
7447
7448         /**
7449          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7450          * @param {String} selector The CSS selector
7451          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7452          * @return {CompositeElement/CompositeElementLite} The composite element
7453          */
7454         select : function(selector, unique){
7455             return El.select(selector, unique, this.dom);
7456         },
7457
7458         /**
7459          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @return {Array} An array of the matched nodes
7462          */
7463         query : function(selector, unique){
7464             return Roo.DomQuery.select(selector, this.dom);
7465         },
7466
7467         /**
7468          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7469          * @param {String} selector The CSS selector
7470          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7471          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7472          */
7473         child : function(selector, returnDom){
7474             var n = Roo.DomQuery.selectNode(selector, this.dom);
7475             return returnDom ? n : Roo.get(n);
7476         },
7477
7478         /**
7479          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7480          * @param {String} selector The CSS selector
7481          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7482          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7483          */
7484         down : function(selector, returnDom){
7485             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7486             return returnDom ? n : Roo.get(n);
7487         },
7488
7489         /**
7490          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7491          * @param {String} group The group the DD object is member of
7492          * @param {Object} config The DD config object
7493          * @param {Object} overrides An object containing methods to override/implement on the DD object
7494          * @return {Roo.dd.DD} The DD object
7495          */
7496         initDD : function(group, config, overrides){
7497             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7498             return Roo.apply(dd, overrides);
7499         },
7500
7501         /**
7502          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7503          * @param {String} group The group the DDProxy object is member of
7504          * @param {Object} config The DDProxy config object
7505          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7506          * @return {Roo.dd.DDProxy} The DDProxy object
7507          */
7508         initDDProxy : function(group, config, overrides){
7509             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7510             return Roo.apply(dd, overrides);
7511         },
7512
7513         /**
7514          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7515          * @param {String} group The group the DDTarget object is member of
7516          * @param {Object} config The DDTarget config object
7517          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7518          * @return {Roo.dd.DDTarget} The DDTarget object
7519          */
7520         initDDTarget : function(group, config, overrides){
7521             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7522             return Roo.apply(dd, overrides);
7523         },
7524
7525         /**
7526          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7527          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7528          * @param {Boolean} visible Whether the element is visible
7529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7530          * @return {Roo.Element} this
7531          */
7532          setVisible : function(visible, animate){
7533             if(!animate || !A){
7534                 if(this.visibilityMode == El.DISPLAY){
7535                     this.setDisplayed(visible);
7536                 }else{
7537                     this.fixDisplay();
7538                     this.dom.style.visibility = visible ? "visible" : "hidden";
7539                 }
7540             }else{
7541                 // closure for composites
7542                 var dom = this.dom;
7543                 var visMode = this.visibilityMode;
7544                 if(visible){
7545                     this.setOpacity(.01);
7546                     this.setVisible(true);
7547                 }
7548                 this.anim({opacity: { to: (visible?1:0) }},
7549                       this.preanim(arguments, 1),
7550                       null, .35, 'easeIn', function(){
7551                          if(!visible){
7552                              if(visMode == El.DISPLAY){
7553                                  dom.style.display = "none";
7554                              }else{
7555                                  dom.style.visibility = "hidden";
7556                              }
7557                              Roo.get(dom).setOpacity(1);
7558                          }
7559                      });
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * Returns true if display is not "none"
7566          * @return {Boolean}
7567          */
7568         isDisplayed : function() {
7569             return this.getStyle("display") != "none";
7570         },
7571
7572         /**
7573          * Toggles the element's visibility or display, depending on visibility mode.
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577         toggle : function(animate){
7578             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7579             return this;
7580         },
7581
7582         /**
7583          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7584          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7585          * @return {Roo.Element} this
7586          */
7587         setDisplayed : function(value) {
7588             if(typeof value == "boolean"){
7589                value = value ? this.originalDisplay : "none";
7590             }
7591             this.setStyle("display", value);
7592             return this;
7593         },
7594
7595         /**
7596          * Tries to focus the element. Any exceptions are caught and ignored.
7597          * @return {Roo.Element} this
7598          */
7599         focus : function() {
7600             try{
7601                 this.dom.focus();
7602             }catch(e){}
7603             return this;
7604         },
7605
7606         /**
7607          * Tries to blur the element. Any exceptions are caught and ignored.
7608          * @return {Roo.Element} this
7609          */
7610         blur : function() {
7611             try{
7612                 this.dom.blur();
7613             }catch(e){}
7614             return this;
7615         },
7616
7617         /**
7618          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7619          * @param {String/Array} className The CSS class to add, or an array of classes
7620          * @return {Roo.Element} this
7621          */
7622         addClass : function(className){
7623             if(className instanceof Array){
7624                 for(var i = 0, len = className.length; i < len; i++) {
7625                     this.addClass(className[i]);
7626                 }
7627             }else{
7628                 if(className && !this.hasClass(className)){
7629                     this.dom.className = this.dom.className + " " + className;
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7637          * @param {String/Array} className The CSS class to add, or an array of classes
7638          * @return {Roo.Element} this
7639          */
7640         radioClass : function(className){
7641             var siblings = this.dom.parentNode.childNodes;
7642             for(var i = 0; i < siblings.length; i++) {
7643                 var s = siblings[i];
7644                 if(s.nodeType == 1){
7645                     Roo.get(s).removeClass(className);
7646                 }
7647             }
7648             this.addClass(className);
7649             return this;
7650         },
7651
7652         /**
7653          * Removes one or more CSS classes from the element.
7654          * @param {String/Array} className The CSS class to remove, or an array of classes
7655          * @return {Roo.Element} this
7656          */
7657         removeClass : function(className){
7658             if(!className || !this.dom.className){
7659                 return this;
7660             }
7661             if(className instanceof Array){
7662                 for(var i = 0, len = className.length; i < len; i++) {
7663                     this.removeClass(className[i]);
7664                 }
7665             }else{
7666                 if(this.hasClass(className)){
7667                     var re = this.classReCache[className];
7668                     if (!re) {
7669                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7670                        this.classReCache[className] = re;
7671                     }
7672                     this.dom.className =
7673                         this.dom.className.replace(re, " ");
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         // private
7680         classReCache: {},
7681
7682         /**
7683          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7684          * @param {String} className The CSS class to toggle
7685          * @return {Roo.Element} this
7686          */
7687         toggleClass : function(className){
7688             if(this.hasClass(className)){
7689                 this.removeClass(className);
7690             }else{
7691                 this.addClass(className);
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Checks if the specified CSS class exists on this element's DOM node.
7698          * @param {String} className The CSS class to check for
7699          * @return {Boolean} True if the class exists, else false
7700          */
7701         hasClass : function(className){
7702             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7703         },
7704
7705         /**
7706          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7707          * @param {String} oldClassName The CSS class to replace
7708          * @param {String} newClassName The replacement CSS class
7709          * @return {Roo.Element} this
7710          */
7711         replaceClass : function(oldClassName, newClassName){
7712             this.removeClass(oldClassName);
7713             this.addClass(newClassName);
7714             return this;
7715         },
7716
7717         /**
7718          * Returns an object with properties matching the styles requested.
7719          * For example, el.getStyles('color', 'font-size', 'width') might return
7720          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7721          * @param {String} style1 A style name
7722          * @param {String} style2 A style name
7723          * @param {String} etc.
7724          * @return {Object} The style object
7725          */
7726         getStyles : function(){
7727             var a = arguments, len = a.length, r = {};
7728             for(var i = 0; i < len; i++){
7729                 r[a[i]] = this.getStyle(a[i]);
7730             }
7731             return r;
7732         },
7733
7734         /**
7735          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7736          * @param {String} property The style property whose value is returned.
7737          * @return {String} The current value of the style property for this element.
7738          */
7739         getStyle : function(){
7740             return view && view.getComputedStyle ?
7741                 function(prop){
7742                     var el = this.dom, v, cs, camel;
7743                     if(prop == 'float'){
7744                         prop = "cssFloat";
7745                     }
7746                     if(el.style && (v = el.style[prop])){
7747                         return v;
7748                     }
7749                     if(cs = view.getComputedStyle(el, "")){
7750                         if(!(camel = propCache[prop])){
7751                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7752                         }
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 } :
7757                 function(prop){
7758                     var el = this.dom, v, cs, camel;
7759                     if(prop == 'opacity'){
7760                         if(typeof el.style.filter == 'string'){
7761                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7762                             if(m){
7763                                 var fv = parseFloat(m[1]);
7764                                 if(!isNaN(fv)){
7765                                     return fv ? fv / 100 : 0;
7766                                 }
7767                             }
7768                         }
7769                         return 1;
7770                     }else if(prop == 'float'){
7771                         prop = "styleFloat";
7772                     }
7773                     if(!(camel = propCache[prop])){
7774                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                     }
7776                     if(v = el.style[camel]){
7777                         return v;
7778                     }
7779                     if(cs = el.currentStyle){
7780                         return cs[camel];
7781                     }
7782                     return null;
7783                 };
7784         }(),
7785
7786         /**
7787          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7788          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7789          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7790          * @return {Roo.Element} this
7791          */
7792         setStyle : function(prop, value){
7793             if(typeof prop == "string"){
7794                 
7795                 if (prop == 'float') {
7796                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7797                     return this;
7798                 }
7799                 
7800                 var camel;
7801                 if(!(camel = propCache[prop])){
7802                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                 }
7804                 
7805                 if(camel == 'opacity') {
7806                     this.setOpacity(value);
7807                 }else{
7808                     this.dom.style[camel] = value;
7809                 }
7810             }else{
7811                 for(var style in prop){
7812                     if(typeof prop[style] != "function"){
7813                        this.setStyle(style, prop[style]);
7814                     }
7815                 }
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * More flexible version of {@link #setStyle} for setting style properties.
7822          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7823          * a function which returns such a specification.
7824          * @return {Roo.Element} this
7825          */
7826         applyStyles : function(style){
7827             Roo.DomHelper.applyStyles(this.dom, style);
7828             return this;
7829         },
7830
7831         /**
7832           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7833           * @return {Number} The X position of the element
7834           */
7835         getX : function(){
7836             return D.getX(this.dom);
7837         },
7838
7839         /**
7840           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7841           * @return {Number} The Y position of the element
7842           */
7843         getY : function(){
7844             return D.getY(this.dom);
7845         },
7846
7847         /**
7848           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7849           * @return {Array} The XY position of the element
7850           */
7851         getXY : function(){
7852             return D.getXY(this.dom);
7853         },
7854
7855         /**
7856          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7857          * @param {Number} The X position of the element
7858          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setX : function(x, animate){
7862             if(!animate || !A){
7863                 D.setX(this.dom, x);
7864             }else{
7865                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7866             }
7867             return this;
7868         },
7869
7870         /**
7871          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7872          * @param {Number} The Y position of the element
7873          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7874          * @return {Roo.Element} this
7875          */
7876         setY : function(y, animate){
7877             if(!animate || !A){
7878                 D.setY(this.dom, y);
7879             }else{
7880                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7887          * @param {String} left The left CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setLeft : function(left){
7891             this.setStyle("left", this.addUnits(left));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7897          * @param {String} top The top CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setTop : function(top){
7901             this.setStyle("top", this.addUnits(top));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the element's CSS right style.
7907          * @param {String} right The right CSS property value
7908          * @return {Roo.Element} this
7909          */
7910         setRight : function(right){
7911             this.setStyle("right", this.addUnits(right));
7912             return this;
7913         },
7914
7915         /**
7916          * Sets the element's CSS bottom style.
7917          * @param {String} bottom The bottom CSS property value
7918          * @return {Roo.Element} this
7919          */
7920         setBottom : function(bottom){
7921             this.setStyle("bottom", this.addUnits(bottom));
7922             return this;
7923         },
7924
7925         /**
7926          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7927          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7928          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7929          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932         setXY : function(pos, animate){
7933             if(!animate || !A){
7934                 D.setXY(this.dom, pos);
7935             }else{
7936                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7943          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7944          * @param {Number} x X value for new position (coordinates are page-based)
7945          * @param {Number} y Y value for new position (coordinates are page-based)
7946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7947          * @return {Roo.Element} this
7948          */
7949         setLocation : function(x, y, animate){
7950             this.setXY([x, y], this.preanim(arguments, 2));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7956          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7957          * @param {Number} x X value for new position (coordinates are page-based)
7958          * @param {Number} y Y value for new position (coordinates are page-based)
7959          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962         moveTo : function(x, y, animate){
7963             this.setXY([x, y], this.preanim(arguments, 2));
7964             return this;
7965         },
7966
7967         /**
7968          * Returns the region of the given element.
7969          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7970          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7971          */
7972         getRegion : function(){
7973             return D.getRegion(this.dom);
7974         },
7975
7976         /**
7977          * Returns the offset height of the element
7978          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7979          * @return {Number} The element's height
7980          */
7981         getHeight : function(contentHeight){
7982             var h = this.dom.offsetHeight || 0;
7983             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7984         },
7985
7986         /**
7987          * Returns the offset width of the element
7988          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7989          * @return {Number} The element's width
7990          */
7991         getWidth : function(contentWidth){
7992             var w = this.dom.offsetWidth || 0;
7993             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7994         },
7995
7996         /**
7997          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7998          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7999          * if a height has not been set using CSS.
8000          * @return {Number}
8001          */
8002         getComputedHeight : function(){
8003             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8004             if(!h){
8005                 h = parseInt(this.getStyle('height'), 10) || 0;
8006                 if(!this.isBorderBox()){
8007                     h += this.getFrameWidth('tb');
8008                 }
8009             }
8010             return h;
8011         },
8012
8013         /**
8014          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8015          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8016          * if a width has not been set using CSS.
8017          * @return {Number}
8018          */
8019         getComputedWidth : function(){
8020             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8021             if(!w){
8022                 w = parseInt(this.getStyle('width'), 10) || 0;
8023                 if(!this.isBorderBox()){
8024                     w += this.getFrameWidth('lr');
8025                 }
8026             }
8027             return w;
8028         },
8029
8030         /**
8031          * Returns the size of the element.
8032          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8033          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8034          */
8035         getSize : function(contentSize){
8036             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8037         },
8038
8039         /**
8040          * Returns the width and height of the viewport.
8041          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8042          */
8043         getViewSize : function(){
8044             var d = this.dom, doc = document, aw = 0, ah = 0;
8045             if(d == doc || d == doc.body){
8046                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8047             }else{
8048                 return {
8049                     width : d.clientWidth,
8050                     height: d.clientHeight
8051                 };
8052             }
8053         },
8054
8055         /**
8056          * Returns the value of the "value" attribute
8057          * @param {Boolean} asNumber true to parse the value as a number
8058          * @return {String/Number}
8059          */
8060         getValue : function(asNumber){
8061             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8062         },
8063
8064         // private
8065         adjustWidth : function(width){
8066             if(typeof width == "number"){
8067                 if(this.autoBoxAdjust && !this.isBorderBox()){
8068                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8069                 }
8070                 if(width < 0){
8071                     width = 0;
8072                 }
8073             }
8074             return width;
8075         },
8076
8077         // private
8078         adjustHeight : function(height){
8079             if(typeof height == "number"){
8080                if(this.autoBoxAdjust && !this.isBorderBox()){
8081                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8082                }
8083                if(height < 0){
8084                    height = 0;
8085                }
8086             }
8087             return height;
8088         },
8089
8090         /**
8091          * Set the width of the element
8092          * @param {Number} width The new width
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096         setWidth : function(width, animate){
8097             width = this.adjustWidth(width);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100             }else{
8101                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Set the height of the element
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112          setHeight : function(height, animate){
8113             height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8124          * @param {Number} width The new width
8125          * @param {Number} height The new height
8126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8127          * @return {Roo.Element} this
8128          */
8129          setSize : function(width, height, animate){
8130             if(typeof width == "object"){ // in case of object from getSize()
8131                 height = width.height; width = width.width;
8132             }
8133             width = this.adjustWidth(width); height = this.adjustHeight(height);
8134             if(!animate || !A){
8135                 this.dom.style.width = this.addUnits(width);
8136                 this.dom.style.height = this.addUnits(height);
8137             }else{
8138                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8145          * @param {Number} x X value for new position (coordinates are page-based)
8146          * @param {Number} y Y value for new position (coordinates are page-based)
8147          * @param {Number} width The new width
8148          * @param {Number} height The new height
8149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8150          * @return {Roo.Element} this
8151          */
8152         setBounds : function(x, y, width, height, animate){
8153             if(!animate || !A){
8154                 this.setSize(width, height);
8155                 this.setLocation(x, y);
8156             }else{
8157                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8158                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8159                               this.preanim(arguments, 4), 'motion');
8160             }
8161             return this;
8162         },
8163
8164         /**
8165          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8166          * @param {Roo.lib.Region} region The region to fill
8167          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8168          * @return {Roo.Element} this
8169          */
8170         setRegion : function(region, animate){
8171             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8172             return this;
8173         },
8174
8175         /**
8176          * Appends an event handler
8177          *
8178          * @param {String}   eventName     The type of event to append
8179          * @param {Function} fn        The method the event invokes
8180          * @param {Object} scope       (optional) The scope (this object) of the fn
8181          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8182          */
8183         addListener : function(eventName, fn, scope, options){
8184             if (this.dom) {
8185                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8186             }
8187         },
8188
8189         /**
8190          * Removes an event handler from this element
8191          * @param {String} eventName the type of event to remove
8192          * @param {Function} fn the method the event invokes
8193          * @return {Roo.Element} this
8194          */
8195         removeListener : function(eventName, fn){
8196             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8197             return this;
8198         },
8199
8200         /**
8201          * Removes all previous added listeners from this element
8202          * @return {Roo.Element} this
8203          */
8204         removeAllListeners : function(){
8205             E.purgeElement(this.dom);
8206             return this;
8207         },
8208
8209         relayEvent : function(eventName, observable){
8210             this.on(eventName, function(e){
8211                 observable.fireEvent(eventName, e);
8212             });
8213         },
8214
8215         /**
8216          * Set the opacity of the element
8217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8218          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8219          * @return {Roo.Element} this
8220          */
8221          setOpacity : function(opacity, animate){
8222             if(!animate || !A){
8223                 var s = this.dom.style;
8224                 if(Roo.isIE){
8225                     s.zoom = 1;
8226                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8227                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8228                 }else{
8229                     s.opacity = opacity;
8230                 }
8231             }else{
8232                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8233             }
8234             return this;
8235         },
8236
8237         /**
8238          * Gets the left X coordinate
8239          * @param {Boolean} local True to get the local css position instead of page coordinate
8240          * @return {Number}
8241          */
8242         getLeft : function(local){
8243             if(!local){
8244                 return this.getX();
8245             }else{
8246                 return parseInt(this.getStyle("left"), 10) || 0;
8247             }
8248         },
8249
8250         /**
8251          * Gets the right X coordinate of the element (element X position + element width)
8252          * @param {Boolean} local True to get the local css position instead of page coordinate
8253          * @return {Number}
8254          */
8255         getRight : function(local){
8256             if(!local){
8257                 return this.getX() + this.getWidth();
8258             }else{
8259                 return (this.getLeft(true) + this.getWidth()) || 0;
8260             }
8261         },
8262
8263         /**
8264          * Gets the top Y coordinate
8265          * @param {Boolean} local True to get the local css position instead of page coordinate
8266          * @return {Number}
8267          */
8268         getTop : function(local) {
8269             if(!local){
8270                 return this.getY();
8271             }else{
8272                 return parseInt(this.getStyle("top"), 10) || 0;
8273             }
8274         },
8275
8276         /**
8277          * Gets the bottom Y coordinate of the element (element Y position + element height)
8278          * @param {Boolean} local True to get the local css position instead of page coordinate
8279          * @return {Number}
8280          */
8281         getBottom : function(local){
8282             if(!local){
8283                 return this.getY() + this.getHeight();
8284             }else{
8285                 return (this.getTop(true) + this.getHeight()) || 0;
8286             }
8287         },
8288
8289         /**
8290         * Initializes positioning on this element. If a desired position is not passed, it will make the
8291         * the element positioned relative IF it is not already positioned.
8292         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8293         * @param {Number} zIndex (optional) The zIndex to apply
8294         * @param {Number} x (optional) Set the page X position
8295         * @param {Number} y (optional) Set the page Y position
8296         */
8297         position : function(pos, zIndex, x, y){
8298             if(!pos){
8299                if(this.getStyle('position') == 'static'){
8300                    this.setStyle('position', 'relative');
8301                }
8302             }else{
8303                 this.setStyle("position", pos);
8304             }
8305             if(zIndex){
8306                 this.setStyle("z-index", zIndex);
8307             }
8308             if(x !== undefined && y !== undefined){
8309                 this.setXY([x, y]);
8310             }else if(x !== undefined){
8311                 this.setX(x);
8312             }else if(y !== undefined){
8313                 this.setY(y);
8314             }
8315         },
8316
8317         /**
8318         * Clear positioning back to the default when the document was loaded
8319         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8320         * @return {Roo.Element} this
8321          */
8322         clearPositioning : function(value){
8323             value = value ||'';
8324             this.setStyle({
8325                 "left": value,
8326                 "right": value,
8327                 "top": value,
8328                 "bottom": value,
8329                 "z-index": "",
8330                 "position" : "static"
8331             });
8332             return this;
8333         },
8334
8335         /**
8336         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8337         * snapshot before performing an update and then restoring the element.
8338         * @return {Object}
8339         */
8340         getPositioning : function(){
8341             var l = this.getStyle("left");
8342             var t = this.getStyle("top");
8343             return {
8344                 "position" : this.getStyle("position"),
8345                 "left" : l,
8346                 "right" : l ? "" : this.getStyle("right"),
8347                 "top" : t,
8348                 "bottom" : t ? "" : this.getStyle("bottom"),
8349                 "z-index" : this.getStyle("z-index")
8350             };
8351         },
8352
8353         /**
8354          * Gets the width of the border(s) for the specified side(s)
8355          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8356          * passing lr would get the border (l)eft width + the border (r)ight width.
8357          * @return {Number} The width of the sides passed added together
8358          */
8359         getBorderWidth : function(side){
8360             return this.addStyles(side, El.borders);
8361         },
8362
8363         /**
8364          * Gets the width of the padding(s) for the specified side(s)
8365          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8366          * passing lr would get the padding (l)eft + the padding (r)ight.
8367          * @return {Number} The padding of the sides passed added together
8368          */
8369         getPadding : function(side){
8370             return this.addStyles(side, El.paddings);
8371         },
8372
8373         /**
8374         * Set positioning with an object returned by getPositioning().
8375         * @param {Object} posCfg
8376         * @return {Roo.Element} this
8377          */
8378         setPositioning : function(pc){
8379             this.applyStyles(pc);
8380             if(pc.right == "auto"){
8381                 this.dom.style.right = "";
8382             }
8383             if(pc.bottom == "auto"){
8384                 this.dom.style.bottom = "";
8385             }
8386             return this;
8387         },
8388
8389         // private
8390         fixDisplay : function(){
8391             if(this.getStyle("display") == "none"){
8392                 this.setStyle("visibility", "hidden");
8393                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8394                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8395                     this.setStyle("display", "block");
8396                 }
8397             }
8398         },
8399
8400         /**
8401          * Quick set left and top adding default units
8402          * @param {String} left The left CSS property value
8403          * @param {String} top The top CSS property value
8404          * @return {Roo.Element} this
8405          */
8406          setLeftTop : function(left, top){
8407             this.dom.style.left = this.addUnits(left);
8408             this.dom.style.top = this.addUnits(top);
8409             return this;
8410         },
8411
8412         /**
8413          * Move this element relative to its current position.
8414          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8415          * @param {Number} distance How far to move the element in pixels
8416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8417          * @return {Roo.Element} this
8418          */
8419          move : function(direction, distance, animate){
8420             var xy = this.getXY();
8421             direction = direction.toLowerCase();
8422             switch(direction){
8423                 case "l":
8424                 case "left":
8425                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8426                     break;
8427                case "r":
8428                case "right":
8429                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8430                     break;
8431                case "t":
8432                case "top":
8433                case "up":
8434                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8435                     break;
8436                case "b":
8437                case "bottom":
8438                case "down":
8439                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8440                     break;
8441             }
8442             return this;
8443         },
8444
8445         /**
8446          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8447          * @return {Roo.Element} this
8448          */
8449         clip : function(){
8450             if(!this.isClipped){
8451                this.isClipped = true;
8452                this.originalClip = {
8453                    "o": this.getStyle("overflow"),
8454                    "x": this.getStyle("overflow-x"),
8455                    "y": this.getStyle("overflow-y")
8456                };
8457                this.setStyle("overflow", "hidden");
8458                this.setStyle("overflow-x", "hidden");
8459                this.setStyle("overflow-y", "hidden");
8460             }
8461             return this;
8462         },
8463
8464         /**
8465          *  Return clipping (overflow) to original clipping before clip() was called
8466          * @return {Roo.Element} this
8467          */
8468         unclip : function(){
8469             if(this.isClipped){
8470                 this.isClipped = false;
8471                 var o = this.originalClip;
8472                 if(o.o){this.setStyle("overflow", o.o);}
8473                 if(o.x){this.setStyle("overflow-x", o.x);}
8474                 if(o.y){this.setStyle("overflow-y", o.y);}
8475             }
8476             return this;
8477         },
8478
8479
8480         /**
8481          * Gets the x,y coordinates specified by the anchor position on the element.
8482          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8483          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8484          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8485          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8486          * @return {Array} [x, y] An array containing the element's x and y coordinates
8487          */
8488         getAnchorXY : function(anchor, local, s){
8489             //Passing a different size is useful for pre-calculating anchors,
8490             //especially for anchored animations that change the el size.
8491
8492             var w, h, vp = false;
8493             if(!s){
8494                 var d = this.dom;
8495                 if(d == document.body || d == document){
8496                     vp = true;
8497                     w = D.getViewWidth(); h = D.getViewHeight();
8498                 }else{
8499                     w = this.getWidth(); h = this.getHeight();
8500                 }
8501             }else{
8502                 w = s.width;  h = s.height;
8503             }
8504             var x = 0, y = 0, r = Math.round;
8505             switch((anchor || "tl").toLowerCase()){
8506                 case "c":
8507                     x = r(w*.5);
8508                     y = r(h*.5);
8509                 break;
8510                 case "t":
8511                     x = r(w*.5);
8512                     y = 0;
8513                 break;
8514                 case "l":
8515                     x = 0;
8516                     y = r(h*.5);
8517                 break;
8518                 case "r":
8519                     x = w;
8520                     y = r(h*.5);
8521                 break;
8522                 case "b":
8523                     x = r(w*.5);
8524                     y = h;
8525                 break;
8526                 case "tl":
8527                     x = 0;
8528                     y = 0;
8529                 break;
8530                 case "bl":
8531                     x = 0;
8532                     y = h;
8533                 break;
8534                 case "br":
8535                     x = w;
8536                     y = h;
8537                 break;
8538                 case "tr":
8539                     x = w;
8540                     y = 0;
8541                 break;
8542             }
8543             if(local === true){
8544                 return [x, y];
8545             }
8546             if(vp){
8547                 var sc = this.getScroll();
8548                 return [x + sc.left, y + sc.top];
8549             }
8550             //Add the element's offset xy
8551             var o = this.getXY();
8552             return [x+o[0], y+o[1]];
8553         },
8554
8555         /**
8556          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8557          * supported position values.
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @return {Array} [x, y]
8562          */
8563         getAlignToXY : function(el, p, o){
8564             el = Roo.get(el);
8565             var d = this.dom;
8566             if(!el.dom){
8567                 throw "Element.alignTo with an element that doesn't exist";
8568             }
8569             var c = false; //constrain to viewport
8570             var p1 = "", p2 = "";
8571             o = o || [0,0];
8572
8573             if(!p){
8574                 p = "tl-bl";
8575             }else if(p == "?"){
8576                 p = "tl-bl?";
8577             }else if(p.indexOf("-") == -1){
8578                 p = "tl-" + p;
8579             }
8580             p = p.toLowerCase();
8581             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8582             if(!m){
8583                throw "Element.alignTo with an invalid alignment " + p;
8584             }
8585             p1 = m[1]; p2 = m[2]; c = !!m[3];
8586
8587             //Subtract the aligned el's internal xy from the target's offset xy
8588             //plus custom offset to get the aligned el's new offset xy
8589             var a1 = this.getAnchorXY(p1, true);
8590             var a2 = el.getAnchorXY(p2, false);
8591             var x = a2[0] - a1[0] + o[0];
8592             var y = a2[1] - a1[1] + o[1];
8593             if(c){
8594                 //constrain the aligned el to viewport if necessary
8595                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8596                 // 5px of margin for ie
8597                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8598
8599                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8600                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8601                 //otherwise swap the aligned el to the opposite border of the target.
8602                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8603                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8604                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8605                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8606
8607                var doc = document;
8608                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8609                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8610
8611                if((x+w) > dw + scrollX){
8612                     x = swapX ? r.left-w : dw+scrollX-w;
8613                 }
8614                if(x < scrollX){
8615                    x = swapX ? r.right : scrollX;
8616                }
8617                if((y+h) > dh + scrollY){
8618                     y = swapY ? r.top-h : dh+scrollY-h;
8619                 }
8620                if (y < scrollY){
8621                    y = swapY ? r.bottom : scrollY;
8622                }
8623             }
8624             return [x,y];
8625         },
8626
8627         // private
8628         getConstrainToXY : function(){
8629             var os = {top:0, left:0, bottom:0, right: 0};
8630
8631             return function(el, local, offsets, proposedXY){
8632                 el = Roo.get(el);
8633                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8634
8635                 var vw, vh, vx = 0, vy = 0;
8636                 if(el.dom == document.body || el.dom == document){
8637                     vw = Roo.lib.Dom.getViewWidth();
8638                     vh = Roo.lib.Dom.getViewHeight();
8639                 }else{
8640                     vw = el.dom.clientWidth;
8641                     vh = el.dom.clientHeight;
8642                     if(!local){
8643                         var vxy = el.getXY();
8644                         vx = vxy[0];
8645                         vy = vxy[1];
8646                     }
8647                 }
8648
8649                 var s = el.getScroll();
8650
8651                 vx += offsets.left + s.left;
8652                 vy += offsets.top + s.top;
8653
8654                 vw -= offsets.right;
8655                 vh -= offsets.bottom;
8656
8657                 var vr = vx+vw;
8658                 var vb = vy+vh;
8659
8660                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8661                 var x = xy[0], y = xy[1];
8662                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8663
8664                 // only move it if it needs it
8665                 var moved = false;
8666
8667                 // first validate right/bottom
8668                 if((x + w) > vr){
8669                     x = vr - w;
8670                     moved = true;
8671                 }
8672                 if((y + h) > vb){
8673                     y = vb - h;
8674                     moved = true;
8675                 }
8676                 // then make sure top/left isn't negative
8677                 if(x < vx){
8678                     x = vx;
8679                     moved = true;
8680                 }
8681                 if(y < vy){
8682                     y = vy;
8683                     moved = true;
8684                 }
8685                 return moved ? [x, y] : false;
8686             };
8687         }(),
8688
8689         // private
8690         adjustForConstraints : function(xy, parent, offsets){
8691             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8692         },
8693
8694         /**
8695          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8696          * document it aligns it to the viewport.
8697          * The position parameter is optional, and can be specified in any one of the following formats:
8698          * <ul>
8699          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8700          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8701          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8702          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8703          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8704          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8705          * </ul>
8706          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8707          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8708          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8709          * that specified in order to enforce the viewport constraints.
8710          * Following are all of the supported anchor positions:
8711     <pre>
8712     Value  Description
8713     -----  -----------------------------
8714     tl     The top left corner (default)
8715     t      The center of the top edge
8716     tr     The top right corner
8717     l      The center of the left edge
8718     c      In the center of the element
8719     r      The center of the right edge
8720     bl     The bottom left corner
8721     b      The center of the bottom edge
8722     br     The bottom right corner
8723     </pre>
8724     Example Usage:
8725     <pre><code>
8726     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8727     el.alignTo("other-el");
8728
8729     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8730     el.alignTo("other-el", "tr?");
8731
8732     // align the bottom right corner of el with the center left edge of other-el
8733     el.alignTo("other-el", "br-l?");
8734
8735     // align the center of el with the bottom left corner of other-el and
8736     // adjust the x position by -6 pixels (and the y position by 0)
8737     el.alignTo("other-el", "c-bl", [-6, 0]);
8738     </code></pre>
8739          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8740          * @param {String} position The position to align to.
8741          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8743          * @return {Roo.Element} this
8744          */
8745         alignTo : function(element, position, offsets, animate){
8746             var xy = this.getAlignToXY(element, position, offsets);
8747             this.setXY(xy, this.preanim(arguments, 3));
8748             return this;
8749         },
8750
8751         /**
8752          * Anchors an element to another element and realigns it when the window is resized.
8753          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8754          * @param {String} position The position to align to.
8755          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8756          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8757          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8758          * is a number, it is used as the buffer delay (defaults to 50ms).
8759          * @param {Function} callback The function to call after the animation finishes
8760          * @return {Roo.Element} this
8761          */
8762         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8763             var action = function(){
8764                 this.alignTo(el, alignment, offsets, animate);
8765                 Roo.callback(callback, this);
8766             };
8767             Roo.EventManager.onWindowResize(action, this);
8768             var tm = typeof monitorScroll;
8769             if(tm != 'undefined'){
8770                 Roo.EventManager.on(window, 'scroll', action, this,
8771                     {buffer: tm == 'number' ? monitorScroll : 50});
8772             }
8773             action.call(this); // align immediately
8774             return this;
8775         },
8776         /**
8777          * Clears any opacity settings from this element. Required in some cases for IE.
8778          * @return {Roo.Element} this
8779          */
8780         clearOpacity : function(){
8781             if (window.ActiveXObject) {
8782                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8783                     this.dom.style.filter = "";
8784                 }
8785             } else {
8786                 this.dom.style.opacity = "";
8787                 this.dom.style["-moz-opacity"] = "";
8788                 this.dom.style["-khtml-opacity"] = "";
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8796          * @return {Roo.Element} this
8797          */
8798         hide : function(animate){
8799             this.setVisible(false, this.preanim(arguments, 0));
8800             return this;
8801         },
8802
8803         /**
8804         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8805         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8806          * @return {Roo.Element} this
8807          */
8808         show : function(animate){
8809             this.setVisible(true, this.preanim(arguments, 0));
8810             return this;
8811         },
8812
8813         /**
8814          * @private Test if size has a unit, otherwise appends the default
8815          */
8816         addUnits : function(size){
8817             return Roo.Element.addUnits(size, this.defaultUnit);
8818         },
8819
8820         /**
8821          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8822          * @return {Roo.Element} this
8823          */
8824         beginMeasure : function(){
8825             var el = this.dom;
8826             if(el.offsetWidth || el.offsetHeight){
8827                 return this; // offsets work already
8828             }
8829             var changed = [];
8830             var p = this.dom, b = document.body; // start with this element
8831             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8832                 var pe = Roo.get(p);
8833                 if(pe.getStyle('display') == 'none'){
8834                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8835                     p.style.visibility = "hidden";
8836                     p.style.display = "block";
8837                 }
8838                 p = p.parentNode;
8839             }
8840             this._measureChanged = changed;
8841             return this;
8842
8843         },
8844
8845         /**
8846          * Restores displays to before beginMeasure was called
8847          * @return {Roo.Element} this
8848          */
8849         endMeasure : function(){
8850             var changed = this._measureChanged;
8851             if(changed){
8852                 for(var i = 0, len = changed.length; i < len; i++) {
8853                     var r = changed[i];
8854                     r.el.style.visibility = r.visibility;
8855                     r.el.style.display = "none";
8856                 }
8857                 this._measureChanged = null;
8858             }
8859             return this;
8860         },
8861
8862         /**
8863         * Update the innerHTML of this element, optionally searching for and processing scripts
8864         * @param {String} html The new HTML
8865         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8866         * @param {Function} callback For async script loading you can be noticed when the update completes
8867         * @return {Roo.Element} this
8868          */
8869         update : function(html, loadScripts, callback){
8870             if(typeof html == "undefined"){
8871                 html = "";
8872             }
8873             if(loadScripts !== true){
8874                 this.dom.innerHTML = html;
8875                 if(typeof callback == "function"){
8876                     callback();
8877                 }
8878                 return this;
8879             }
8880             var id = Roo.id();
8881             var dom = this.dom;
8882
8883             html += '<span id="' + id + '"></span>';
8884
8885             E.onAvailable(id, function(){
8886                 var hd = document.getElementsByTagName("head")[0];
8887                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8888                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8889                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8890
8891                 var match;
8892                 while(match = re.exec(html)){
8893                     var attrs = match[1];
8894                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8895                     if(srcMatch && srcMatch[2]){
8896                        var s = document.createElement("script");
8897                        s.src = srcMatch[2];
8898                        var typeMatch = attrs.match(typeRe);
8899                        if(typeMatch && typeMatch[2]){
8900                            s.type = typeMatch[2];
8901                        }
8902                        hd.appendChild(s);
8903                     }else if(match[2] && match[2].length > 0){
8904                         if(window.execScript) {
8905                            window.execScript(match[2]);
8906                         } else {
8907                             /**
8908                              * eval:var:id
8909                              * eval:var:dom
8910                              * eval:var:html
8911                              * 
8912                              */
8913                            window.eval(match[2]);
8914                         }
8915                     }
8916                 }
8917                 var el = document.getElementById(id);
8918                 if(el){el.parentNode.removeChild(el);}
8919                 if(typeof callback == "function"){
8920                     callback();
8921                 }
8922             });
8923             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8924             return this;
8925         },
8926
8927         /**
8928          * Direct access to the UpdateManager update() method (takes the same parameters).
8929          * @param {String/Function} url The url for this request or a function to call to get the url
8930          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8931          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8932          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8933          * @return {Roo.Element} this
8934          */
8935         load : function(){
8936             var um = this.getUpdateManager();
8937             um.update.apply(um, arguments);
8938             return this;
8939         },
8940
8941         /**
8942         * Gets this element's UpdateManager
8943         * @return {Roo.UpdateManager} The UpdateManager
8944         */
8945         getUpdateManager : function(){
8946             if(!this.updateManager){
8947                 this.updateManager = new Roo.UpdateManager(this);
8948             }
8949             return this.updateManager;
8950         },
8951
8952         /**
8953          * Disables text selection for this element (normalized across browsers)
8954          * @return {Roo.Element} this
8955          */
8956         unselectable : function(){
8957             this.dom.unselectable = "on";
8958             this.swallowEvent("selectstart", true);
8959             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8960             this.addClass("x-unselectable");
8961             return this;
8962         },
8963
8964         /**
8965         * Calculates the x, y to center this element on the screen
8966         * @return {Array} The x, y values [x, y]
8967         */
8968         getCenterXY : function(){
8969             return this.getAlignToXY(document, 'c-c');
8970         },
8971
8972         /**
8973         * Centers the Element in either the viewport, or another Element.
8974         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8975         */
8976         center : function(centerIn){
8977             this.alignTo(centerIn || document, 'c-c');
8978             return this;
8979         },
8980
8981         /**
8982          * Tests various css rules/browsers to determine if this element uses a border box
8983          * @return {Boolean}
8984          */
8985         isBorderBox : function(){
8986             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8987         },
8988
8989         /**
8990          * Return a box {x, y, width, height} that can be used to set another elements
8991          * size/location to match this element.
8992          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8993          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8994          * @return {Object} box An object in the format {x, y, width, height}
8995          */
8996         getBox : function(contentBox, local){
8997             var xy;
8998             if(!local){
8999                 xy = this.getXY();
9000             }else{
9001                 var left = parseInt(this.getStyle("left"), 10) || 0;
9002                 var top = parseInt(this.getStyle("top"), 10) || 0;
9003                 xy = [left, top];
9004             }
9005             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9006             if(!contentBox){
9007                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9008             }else{
9009                 var l = this.getBorderWidth("l")+this.getPadding("l");
9010                 var r = this.getBorderWidth("r")+this.getPadding("r");
9011                 var t = this.getBorderWidth("t")+this.getPadding("t");
9012                 var b = this.getBorderWidth("b")+this.getPadding("b");
9013                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9014             }
9015             bx.right = bx.x + bx.width;
9016             bx.bottom = bx.y + bx.height;
9017             return bx;
9018         },
9019
9020         /**
9021          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9022          for more information about the sides.
9023          * @param {String} sides
9024          * @return {Number}
9025          */
9026         getFrameWidth : function(sides, onlyContentBox){
9027             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9028         },
9029
9030         /**
9031          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9032          * @param {Object} box The box to fill {x, y, width, height}
9033          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9035          * @return {Roo.Element} this
9036          */
9037         setBox : function(box, adjust, animate){
9038             var w = box.width, h = box.height;
9039             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9040                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9041                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9042             }
9043             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9044             return this;
9045         },
9046
9047         /**
9048          * Forces the browser to repaint this element
9049          * @return {Roo.Element} this
9050          */
9051          repaint : function(){
9052             var dom = this.dom;
9053             this.addClass("x-repaint");
9054             setTimeout(function(){
9055                 Roo.get(dom).removeClass("x-repaint");
9056             }, 1);
9057             return this;
9058         },
9059
9060         /**
9061          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9062          * then it returns the calculated width of the sides (see getPadding)
9063          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9064          * @return {Object/Number}
9065          */
9066         getMargins : function(side){
9067             if(!side){
9068                 return {
9069                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9070                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9071                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9072                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9073                 };
9074             }else{
9075                 return this.addStyles(side, El.margins);
9076              }
9077         },
9078
9079         // private
9080         addStyles : function(sides, styles){
9081             var val = 0, v, w;
9082             for(var i = 0, len = sides.length; i < len; i++){
9083                 v = this.getStyle(styles[sides.charAt(i)]);
9084                 if(v){
9085                      w = parseInt(v, 10);
9086                      if(w){ val += w; }
9087                 }
9088             }
9089             return val;
9090         },
9091
9092         /**
9093          * Creates a proxy element of this element
9094          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9095          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9096          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9097          * @return {Roo.Element} The new proxy element
9098          */
9099         createProxy : function(config, renderTo, matchBox){
9100             if(renderTo){
9101                 renderTo = Roo.getDom(renderTo);
9102             }else{
9103                 renderTo = document.body;
9104             }
9105             config = typeof config == "object" ?
9106                 config : {tag : "div", cls: config};
9107             var proxy = Roo.DomHelper.append(renderTo, config, true);
9108             if(matchBox){
9109                proxy.setBox(this.getBox());
9110             }
9111             return proxy;
9112         },
9113
9114         /**
9115          * Puts a mask over this element to disable user interaction. Requires core.css.
9116          * This method can only be applied to elements which accept child nodes.
9117          * @param {String} msg (optional) A message to display in the mask
9118          * @param {String} msgCls (optional) A css class to apply to the msg element
9119          * @return {Element} The mask  element
9120          */
9121         mask : function(msg, msgCls)
9122         {
9123             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9124                 this.setStyle("position", "relative");
9125             }
9126             if(!this._mask){
9127                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9128             }
9129             
9130             this.addClass("x-masked");
9131             this._mask.setDisplayed(true);
9132             
9133             // we wander
9134             var z = 0;
9135             var dom = this.dom;
9136             while (dom && dom.style) {
9137                 if (!isNaN(parseInt(dom.style.zIndex))) {
9138                     z = Math.max(z, parseInt(dom.style.zIndex));
9139                 }
9140                 dom = dom.parentNode;
9141             }
9142             // if we are masking the body - then it hides everything..
9143             if (this.dom == document.body) {
9144                 z = 1000000;
9145                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9146                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9147             }
9148            
9149             if(typeof msg == 'string'){
9150                 if(!this._maskMsg){
9151                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9152                         cls: "roo-el-mask-msg", 
9153                         cn: [
9154                             {
9155                                 tag: 'i',
9156                                 cls: 'fa fa-spinner fa-spin'
9157                             },
9158                             {
9159                                 tag: 'div'
9160                             }   
9161                         ]
9162                     }, true);
9163                 }
9164                 var mm = this._maskMsg;
9165                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9166                 if (mm.dom.lastChild) { // weird IE issue?
9167                     mm.dom.lastChild.innerHTML = msg;
9168                 }
9169                 mm.setDisplayed(true);
9170                 mm.center(this);
9171                 mm.setStyle('z-index', z + 102);
9172             }
9173             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9174                 this._mask.setHeight(this.getHeight());
9175             }
9176             this._mask.setStyle('z-index', z + 100);
9177             
9178             return this._mask;
9179         },
9180
9181         /**
9182          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9183          * it is cached for reuse.
9184          */
9185         unmask : function(removeEl){
9186             if(this._mask){
9187                 if(removeEl === true){
9188                     this._mask.remove();
9189                     delete this._mask;
9190                     if(this._maskMsg){
9191                         this._maskMsg.remove();
9192                         delete this._maskMsg;
9193                     }
9194                 }else{
9195                     this._mask.setDisplayed(false);
9196                     if(this._maskMsg){
9197                         this._maskMsg.setDisplayed(false);
9198                     }
9199                 }
9200             }
9201             this.removeClass("x-masked");
9202         },
9203
9204         /**
9205          * Returns true if this element is masked
9206          * @return {Boolean}
9207          */
9208         isMasked : function(){
9209             return this._mask && this._mask.isVisible();
9210         },
9211
9212         /**
9213          * Creates an iframe shim for this element to keep selects and other windowed objects from
9214          * showing through.
9215          * @return {Roo.Element} The new shim element
9216          */
9217         createShim : function(){
9218             var el = document.createElement('iframe');
9219             el.frameBorder = 'no';
9220             el.className = 'roo-shim';
9221             if(Roo.isIE && Roo.isSecure){
9222                 el.src = Roo.SSL_SECURE_URL;
9223             }
9224             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9225             shim.autoBoxAdjust = false;
9226             return shim;
9227         },
9228
9229         /**
9230          * Removes this element from the DOM and deletes it from the cache
9231          */
9232         remove : function(){
9233             if(this.dom.parentNode){
9234                 this.dom.parentNode.removeChild(this.dom);
9235             }
9236             delete El.cache[this.dom.id];
9237         },
9238
9239         /**
9240          * Sets up event handlers to add and remove a css class when the mouse is over this element
9241          * @param {String} className
9242          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9243          * mouseout events for children elements
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnOver : function(className, preventFlicker){
9247             this.on("mouseover", function(){
9248                 Roo.fly(this, '_internal').addClass(className);
9249             }, this.dom);
9250             var removeFn = function(e){
9251                 if(preventFlicker !== true || !e.within(this, true)){
9252                     Roo.fly(this, '_internal').removeClass(className);
9253                 }
9254             };
9255             this.on("mouseout", removeFn, this.dom);
9256             return this;
9257         },
9258
9259         /**
9260          * Sets up event handlers to add and remove a css class when this element has the focus
9261          * @param {String} className
9262          * @return {Roo.Element} this
9263          */
9264         addClassOnFocus : function(className){
9265             this.on("focus", function(){
9266                 Roo.fly(this, '_internal').addClass(className);
9267             }, this.dom);
9268             this.on("blur", function(){
9269                 Roo.fly(this, '_internal').removeClass(className);
9270             }, this.dom);
9271             return this;
9272         },
9273         /**
9274          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9275          * @param {String} className
9276          * @return {Roo.Element} this
9277          */
9278         addClassOnClick : function(className){
9279             var dom = this.dom;
9280             this.on("mousedown", function(){
9281                 Roo.fly(dom, '_internal').addClass(className);
9282                 var d = Roo.get(document);
9283                 var fn = function(){
9284                     Roo.fly(dom, '_internal').removeClass(className);
9285                     d.removeListener("mouseup", fn);
9286                 };
9287                 d.on("mouseup", fn);
9288             });
9289             return this;
9290         },
9291
9292         /**
9293          * Stops the specified event from bubbling and optionally prevents the default action
9294          * @param {String} eventName
9295          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9296          * @return {Roo.Element} this
9297          */
9298         swallowEvent : function(eventName, preventDefault){
9299             var fn = function(e){
9300                 e.stopPropagation();
9301                 if(preventDefault){
9302                     e.preventDefault();
9303                 }
9304             };
9305             if(eventName instanceof Array){
9306                 for(var i = 0, len = eventName.length; i < len; i++){
9307                      this.on(eventName[i], fn);
9308                 }
9309                 return this;
9310             }
9311             this.on(eventName, fn);
9312             return this;
9313         },
9314
9315         /**
9316          * @private
9317          */
9318       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9319
9320         /**
9321          * Sizes this element to its parent element's dimensions performing
9322          * neccessary box adjustments.
9323          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9324          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9325          * @return {Roo.Element} this
9326          */
9327         fitToParent : function(monitorResize, targetParent) {
9328           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9329           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9330           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9331             return;
9332           }
9333           var p = Roo.get(targetParent || this.dom.parentNode);
9334           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9335           if (monitorResize === true) {
9336             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9337             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9338           }
9339           return this;
9340         },
9341
9342         /**
9343          * Gets the next sibling, skipping text nodes
9344          * @return {HTMLElement} The next sibling or null
9345          */
9346         getNextSibling : function(){
9347             var n = this.dom.nextSibling;
9348             while(n && n.nodeType != 1){
9349                 n = n.nextSibling;
9350             }
9351             return n;
9352         },
9353
9354         /**
9355          * Gets the previous sibling, skipping text nodes
9356          * @return {HTMLElement} The previous sibling or null
9357          */
9358         getPrevSibling : function(){
9359             var n = this.dom.previousSibling;
9360             while(n && n.nodeType != 1){
9361                 n = n.previousSibling;
9362             }
9363             return n;
9364         },
9365
9366
9367         /**
9368          * Appends the passed element(s) to this element
9369          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9370          * @return {Roo.Element} this
9371          */
9372         appendChild: function(el){
9373             el = Roo.get(el);
9374             el.appendTo(this);
9375             return this;
9376         },
9377
9378         /**
9379          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9380          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9381          * automatically generated with the specified attributes.
9382          * @param {HTMLElement} insertBefore (optional) a child element of this element
9383          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9384          * @return {Roo.Element} The new child element
9385          */
9386         createChild: function(config, insertBefore, returnDom){
9387             config = config || {tag:'div'};
9388             if(insertBefore){
9389                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9390             }
9391             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9392         },
9393
9394         /**
9395          * Appends this element to the passed element
9396          * @param {String/HTMLElement/Element} el The new parent element
9397          * @return {Roo.Element} this
9398          */
9399         appendTo: function(el){
9400             el = Roo.getDom(el);
9401             el.appendChild(this.dom);
9402             return this;
9403         },
9404
9405         /**
9406          * Inserts this element before the passed element in the DOM
9407          * @param {String/HTMLElement/Element} el The element to insert before
9408          * @return {Roo.Element} this
9409          */
9410         insertBefore: function(el){
9411             el = Roo.getDom(el);
9412             el.parentNode.insertBefore(this.dom, el);
9413             return this;
9414         },
9415
9416         /**
9417          * Inserts this element after the passed element in the DOM
9418          * @param {String/HTMLElement/Element} el The element to insert after
9419          * @return {Roo.Element} this
9420          */
9421         insertAfter: function(el){
9422             el = Roo.getDom(el);
9423             el.parentNode.insertBefore(this.dom, el.nextSibling);
9424             return this;
9425         },
9426
9427         /**
9428          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9429          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9430          * @return {Roo.Element} The new child
9431          */
9432         insertFirst: function(el, returnDom){
9433             el = el || {};
9434             if(typeof el == 'object' && !el.nodeType){ // dh config
9435                 return this.createChild(el, this.dom.firstChild, returnDom);
9436             }else{
9437                 el = Roo.getDom(el);
9438                 this.dom.insertBefore(el, this.dom.firstChild);
9439                 return !returnDom ? Roo.get(el) : el;
9440             }
9441         },
9442
9443         /**
9444          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9445          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9446          * @param {String} where (optional) 'before' or 'after' defaults to before
9447          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9448          * @return {Roo.Element} the inserted Element
9449          */
9450         insertSibling: function(el, where, returnDom){
9451             where = where ? where.toLowerCase() : 'before';
9452             el = el || {};
9453             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9454
9455             if(typeof el == 'object' && !el.nodeType){ // dh config
9456                 if(where == 'after' && !this.dom.nextSibling){
9457                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9458                 }else{
9459                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9460                 }
9461
9462             }else{
9463                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9464                             where == 'before' ? this.dom : this.dom.nextSibling);
9465                 if(!returnDom){
9466                     rt = Roo.get(rt);
9467                 }
9468             }
9469             return rt;
9470         },
9471
9472         /**
9473          * Creates and wraps this element with another element
9474          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9475          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9476          * @return {HTMLElement/Element} The newly created wrapper element
9477          */
9478         wrap: function(config, returnDom){
9479             if(!config){
9480                 config = {tag: "div"};
9481             }
9482             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9483             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9484             return newEl;
9485         },
9486
9487         /**
9488          * Replaces the passed element with this element
9489          * @param {String/HTMLElement/Element} el The element to replace
9490          * @return {Roo.Element} this
9491          */
9492         replace: function(el){
9493             el = Roo.get(el);
9494             this.insertBefore(el);
9495             el.remove();
9496             return this;
9497         },
9498
9499         /**
9500          * Inserts an html fragment into this element
9501          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9502          * @param {String} html The HTML fragment
9503          * @param {Boolean} returnEl True to return an Roo.Element
9504          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9505          */
9506         insertHtml : function(where, html, returnEl){
9507             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9508             return returnEl ? Roo.get(el) : el;
9509         },
9510
9511         /**
9512          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9513          * @param {Object} o The object with the attributes
9514          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9515          * @return {Roo.Element} this
9516          */
9517         set : function(o, useSet){
9518             var el = this.dom;
9519             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9520             for(var attr in o){
9521                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9522                 if(attr=="cls"){
9523                     el.className = o["cls"];
9524                 }else{
9525                     if(useSet) {
9526                         el.setAttribute(attr, o[attr]);
9527                     } else {
9528                         el[attr] = o[attr];
9529                     }
9530                 }
9531             }
9532             if(o.style){
9533                 Roo.DomHelper.applyStyles(el, o.style);
9534             }
9535             return this;
9536         },
9537
9538         /**
9539          * Convenience method for constructing a KeyMap
9540          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9541          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9542          * @param {Function} fn The function to call
9543          * @param {Object} scope (optional) The scope of the function
9544          * @return {Roo.KeyMap} The KeyMap created
9545          */
9546         addKeyListener : function(key, fn, scope){
9547             var config;
9548             if(typeof key != "object" || key instanceof Array){
9549                 config = {
9550                     key: key,
9551                     fn: fn,
9552                     scope: scope
9553                 };
9554             }else{
9555                 config = {
9556                     key : key.key,
9557                     shift : key.shift,
9558                     ctrl : key.ctrl,
9559                     alt : key.alt,
9560                     fn: fn,
9561                     scope: scope
9562                 };
9563             }
9564             return new Roo.KeyMap(this, config);
9565         },
9566
9567         /**
9568          * Creates a KeyMap for this element
9569          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9570          * @return {Roo.KeyMap} The KeyMap created
9571          */
9572         addKeyMap : function(config){
9573             return new Roo.KeyMap(this, config);
9574         },
9575
9576         /**
9577          * Returns true if this element is scrollable.
9578          * @return {Boolean}
9579          */
9580          isScrollable : function(){
9581             var dom = this.dom;
9582             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9583         },
9584
9585         /**
9586          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9587          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9588          * @param {Number} value The new scroll value
9589          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9590          * @return {Element} this
9591          */
9592
9593         scrollTo : function(side, value, animate){
9594             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9595             if(!animate || !A){
9596                 this.dom[prop] = value;
9597             }else{
9598                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9599                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9600             }
9601             return this;
9602         },
9603
9604         /**
9605          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9606          * within this element's scrollable range.
9607          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9608          * @param {Number} distance How far to scroll the element in pixels
9609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9610          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9611          * was scrolled as far as it could go.
9612          */
9613          scroll : function(direction, distance, animate){
9614              if(!this.isScrollable()){
9615                  return;
9616              }
9617              var el = this.dom;
9618              var l = el.scrollLeft, t = el.scrollTop;
9619              var w = el.scrollWidth, h = el.scrollHeight;
9620              var cw = el.clientWidth, ch = el.clientHeight;
9621              direction = direction.toLowerCase();
9622              var scrolled = false;
9623              var a = this.preanim(arguments, 2);
9624              switch(direction){
9625                  case "l":
9626                  case "left":
9627                      if(w - l > cw){
9628                          var v = Math.min(l + distance, w-cw);
9629                          this.scrollTo("left", v, a);
9630                          scrolled = true;
9631                      }
9632                      break;
9633                 case "r":
9634                 case "right":
9635                      if(l > 0){
9636                          var v = Math.max(l - distance, 0);
9637                          this.scrollTo("left", v, a);
9638                          scrolled = true;
9639                      }
9640                      break;
9641                 case "t":
9642                 case "top":
9643                 case "up":
9644                      if(t > 0){
9645                          var v = Math.max(t - distance, 0);
9646                          this.scrollTo("top", v, a);
9647                          scrolled = true;
9648                      }
9649                      break;
9650                 case "b":
9651                 case "bottom":
9652                 case "down":
9653                      if(h - t > ch){
9654                          var v = Math.min(t + distance, h-ch);
9655                          this.scrollTo("top", v, a);
9656                          scrolled = true;
9657                      }
9658                      break;
9659              }
9660              return scrolled;
9661         },
9662
9663         /**
9664          * Translates the passed page coordinates into left/top css values for this element
9665          * @param {Number/Array} x The page x or an array containing [x, y]
9666          * @param {Number} y The page y
9667          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9668          */
9669         translatePoints : function(x, y){
9670             if(typeof x == 'object' || x instanceof Array){
9671                 y = x[1]; x = x[0];
9672             }
9673             var p = this.getStyle('position');
9674             var o = this.getXY();
9675
9676             var l = parseInt(this.getStyle('left'), 10);
9677             var t = parseInt(this.getStyle('top'), 10);
9678
9679             if(isNaN(l)){
9680                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9681             }
9682             if(isNaN(t)){
9683                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9684             }
9685
9686             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9687         },
9688
9689         /**
9690          * Returns the current scroll position of the element.
9691          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9692          */
9693         getScroll : function(){
9694             var d = this.dom, doc = document;
9695             if(d == doc || d == doc.body){
9696                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9697                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9698                 return {left: l, top: t};
9699             }else{
9700                 return {left: d.scrollLeft, top: d.scrollTop};
9701             }
9702         },
9703
9704         /**
9705          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9706          * are convert to standard 6 digit hex color.
9707          * @param {String} attr The css attribute
9708          * @param {String} defaultValue The default value to use when a valid color isn't found
9709          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9710          * YUI color anims.
9711          */
9712         getColor : function(attr, defaultValue, prefix){
9713             var v = this.getStyle(attr);
9714             if(!v || v == "transparent" || v == "inherit") {
9715                 return defaultValue;
9716             }
9717             var color = typeof prefix == "undefined" ? "#" : prefix;
9718             if(v.substr(0, 4) == "rgb("){
9719                 var rvs = v.slice(4, v.length -1).split(",");
9720                 for(var i = 0; i < 3; i++){
9721                     var h = parseInt(rvs[i]).toString(16);
9722                     if(h < 16){
9723                         h = "0" + h;
9724                     }
9725                     color += h;
9726                 }
9727             } else {
9728                 if(v.substr(0, 1) == "#"){
9729                     if(v.length == 4) {
9730                         for(var i = 1; i < 4; i++){
9731                             var c = v.charAt(i);
9732                             color +=  c + c;
9733                         }
9734                     }else if(v.length == 7){
9735                         color += v.substr(1);
9736                     }
9737                 }
9738             }
9739             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9740         },
9741
9742         /**
9743          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9744          * gradient background, rounded corners and a 4-way shadow.
9745          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9746          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9747          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9748          * @return {Roo.Element} this
9749          */
9750         boxWrap : function(cls){
9751             cls = cls || 'x-box';
9752             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9753             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9754             return el;
9755         },
9756
9757         /**
9758          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9759          * @param {String} namespace The namespace in which to look for the attribute
9760          * @param {String} name The attribute name
9761          * @return {String} The attribute value
9762          */
9763         getAttributeNS : Roo.isIE ? function(ns, name){
9764             var d = this.dom;
9765             var type = typeof d[ns+":"+name];
9766             if(type != 'undefined' && type != 'unknown'){
9767                 return d[ns+":"+name];
9768             }
9769             return d[name];
9770         } : function(ns, name){
9771             var d = this.dom;
9772             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9773         },
9774         
9775         
9776         /**
9777          * Sets or Returns the value the dom attribute value
9778          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9779          * @param {String} value (optional) The value to set the attribute to
9780          * @return {String} The attribute value
9781          */
9782         attr : function(name){
9783             if (arguments.length > 1) {
9784                 this.dom.setAttribute(name, arguments[1]);
9785                 return arguments[1];
9786             }
9787             if (typeof(name) == 'object') {
9788                 for(var i in name) {
9789                     this.attr(i, name[i]);
9790                 }
9791                 return name;
9792             }
9793             
9794             
9795             if (!this.dom.hasAttribute(name)) {
9796                 return undefined;
9797             }
9798             return this.dom.getAttribute(name);
9799         }
9800         
9801         
9802         
9803     };
9804
9805     var ep = El.prototype;
9806
9807     /**
9808      * Appends an event handler (Shorthand for addListener)
9809      * @param {String}   eventName     The type of event to append
9810      * @param {Function} fn        The method the event invokes
9811      * @param {Object} scope       (optional) The scope (this object) of the fn
9812      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9813      * @method
9814      */
9815     ep.on = ep.addListener;
9816         // backwards compat
9817     ep.mon = ep.addListener;
9818
9819     /**
9820      * Removes an event handler from this element (shorthand for removeListener)
9821      * @param {String} eventName the type of event to remove
9822      * @param {Function} fn the method the event invokes
9823      * @return {Roo.Element} this
9824      * @method
9825      */
9826     ep.un = ep.removeListener;
9827
9828     /**
9829      * true to automatically adjust width and height settings for box-model issues (default to true)
9830      */
9831     ep.autoBoxAdjust = true;
9832
9833     // private
9834     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9835
9836     // private
9837     El.addUnits = function(v, defaultUnit){
9838         if(v === "" || v == "auto"){
9839             return v;
9840         }
9841         if(v === undefined){
9842             return '';
9843         }
9844         if(typeof v == "number" || !El.unitPattern.test(v)){
9845             return v + (defaultUnit || 'px');
9846         }
9847         return v;
9848     };
9849
9850     // special markup used throughout Roo when box wrapping elements
9851     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9852     /**
9853      * Visibility mode constant - Use visibility to hide element
9854      * @static
9855      * @type Number
9856      */
9857     El.VISIBILITY = 1;
9858     /**
9859      * Visibility mode constant - Use display to hide element
9860      * @static
9861      * @type Number
9862      */
9863     El.DISPLAY = 2;
9864
9865     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9866     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9867     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9868
9869
9870
9871     /**
9872      * @private
9873      */
9874     El.cache = {};
9875
9876     var docEl;
9877
9878     /**
9879      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9880      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @static
9884      */
9885     El.get = function(el){
9886         var ex, elm, id;
9887         if(!el){ return null; }
9888         if(typeof el == "string"){ // element id
9889             if(!(elm = document.getElementById(el))){
9890                 return null;
9891             }
9892             if(ex = El.cache[el]){
9893                 ex.dom = elm;
9894             }else{
9895                 ex = El.cache[el] = new El(elm);
9896             }
9897             return ex;
9898         }else if(el.tagName){ // dom element
9899             if(!(id = el.id)){
9900                 id = Roo.id(el);
9901             }
9902             if(ex = El.cache[id]){
9903                 ex.dom = el;
9904             }else{
9905                 ex = El.cache[id] = new El(el);
9906             }
9907             return ex;
9908         }else if(el instanceof El){
9909             if(el != docEl){
9910                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9911                                                               // catch case where it hasn't been appended
9912                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9913             }
9914             return el;
9915         }else if(el.isComposite){
9916             return el;
9917         }else if(el instanceof Array){
9918             return El.select(el);
9919         }else if(el == document){
9920             // create a bogus element object representing the document object
9921             if(!docEl){
9922                 var f = function(){};
9923                 f.prototype = El.prototype;
9924                 docEl = new f();
9925                 docEl.dom = document;
9926             }
9927             return docEl;
9928         }
9929         return null;
9930     };
9931
9932     // private
9933     El.uncache = function(el){
9934         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9935             if(a[i]){
9936                 delete El.cache[a[i].id || a[i]];
9937             }
9938         }
9939     };
9940
9941     // private
9942     // Garbage collection - uncache elements/purge listeners on orphaned elements
9943     // so we don't hold a reference and cause the browser to retain them
9944     El.garbageCollect = function(){
9945         if(!Roo.enableGarbageCollector){
9946             clearInterval(El.collectorThread);
9947             return;
9948         }
9949         for(var eid in El.cache){
9950             var el = El.cache[eid], d = el.dom;
9951             // -------------------------------------------------------
9952             // Determining what is garbage:
9953             // -------------------------------------------------------
9954             // !d
9955             // dom node is null, definitely garbage
9956             // -------------------------------------------------------
9957             // !d.parentNode
9958             // no parentNode == direct orphan, definitely garbage
9959             // -------------------------------------------------------
9960             // !d.offsetParent && !document.getElementById(eid)
9961             // display none elements have no offsetParent so we will
9962             // also try to look it up by it's id. However, check
9963             // offsetParent first so we don't do unneeded lookups.
9964             // This enables collection of elements that are not orphans
9965             // directly, but somewhere up the line they have an orphan
9966             // parent.
9967             // -------------------------------------------------------
9968             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9969                 delete El.cache[eid];
9970                 if(d && Roo.enableListenerCollection){
9971                     E.purgeElement(d);
9972                 }
9973             }
9974         }
9975     }
9976     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9977
9978
9979     // dom is optional
9980     El.Flyweight = function(dom){
9981         this.dom = dom;
9982     };
9983     El.Flyweight.prototype = El.prototype;
9984
9985     El._flyweights = {};
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      */
9995     El.fly = function(el, named){
9996         named = named || '_global';
9997         el = Roo.getDom(el);
9998         if(!el){
9999             return null;
10000         }
10001         if(!El._flyweights[named]){
10002             El._flyweights[named] = new El.Flyweight();
10003         }
10004         El._flyweights[named].dom = el;
10005         return El._flyweights[named];
10006     };
10007
10008     /**
10009      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10010      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10011      * Shorthand of {@link Roo.Element#get}
10012      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10013      * @return {Element} The Element object
10014      * @member Roo
10015      * @method get
10016      */
10017     Roo.get = El.get;
10018     /**
10019      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10020      * the dom node can be overwritten by other code.
10021      * Shorthand of {@link Roo.Element#fly}
10022      * @param {String/HTMLElement} el The dom node or id
10023      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10024      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10025      * @static
10026      * @return {Element} The shared Element object
10027      * @member Roo
10028      * @method fly
10029      */
10030     Roo.fly = El.fly;
10031
10032     // speedy lookup for elements never to box adjust
10033     var noBoxAdjust = Roo.isStrict ? {
10034         select:1
10035     } : {
10036         input:1, select:1, textarea:1
10037     };
10038     if(Roo.isIE || Roo.isGecko){
10039         noBoxAdjust['button'] = 1;
10040     }
10041
10042
10043     Roo.EventManager.on(window, 'unload', function(){
10044         delete El.cache;
10045         delete El._flyweights;
10046     });
10047 })();
10048
10049
10050
10051
10052 if(Roo.DomQuery){
10053     Roo.Element.selectorFunction = Roo.DomQuery.select;
10054 }
10055
10056 Roo.Element.select = function(selector, unique, root){
10057     var els;
10058     if(typeof selector == "string"){
10059         els = Roo.Element.selectorFunction(selector, root);
10060     }else if(selector.length !== undefined){
10061         els = selector;
10062     }else{
10063         throw "Invalid selector";
10064     }
10065     if(unique === true){
10066         return new Roo.CompositeElement(els);
10067     }else{
10068         return new Roo.CompositeElementLite(els);
10069     }
10070 };
10071 /**
10072  * Selects elements based on the passed CSS selector to enable working on them as 1.
10073  * @param {String/Array} selector The CSS selector or an array of elements
10074  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10075  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10076  * @return {CompositeElementLite/CompositeElement}
10077  * @member Roo
10078  * @method select
10079  */
10080 Roo.select = Roo.Element.select;
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095 /*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105
10106
10107
10108 //Notifies Element that fx methods are available
10109 Roo.enableFx = true;
10110
10111 /**
10112  * @class Roo.Fx
10113  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10114  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10115  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10116  * Element effects to work.</p><br/>
10117  *
10118  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10119  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10120  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10121  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10122  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10123  * expected results and should be done with care.</p><br/>
10124  *
10125  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10126  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10127 <pre>
10128 Value  Description
10129 -----  -----------------------------
10130 tl     The top left corner
10131 t      The center of the top edge
10132 tr     The top right corner
10133 l      The center of the left edge
10134 r      The center of the right edge
10135 bl     The bottom left corner
10136 b      The center of the bottom edge
10137 br     The bottom right corner
10138 </pre>
10139  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10140  * below are common options that can be passed to any Fx method.</b>
10141  * @cfg {Function} callback A function called when the effect is finished
10142  * @cfg {Object} scope The scope of the effect function
10143  * @cfg {String} easing A valid Easing value for the effect
10144  * @cfg {String} afterCls A css class to apply after the effect
10145  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10146  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10147  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10148  * effects that end with the element being visually hidden, ignored otherwise)
10149  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10150  * a function which returns such a specification that will be applied to the Element after the effect finishes
10151  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10152  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10153  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10154  */
10155 Roo.Fx = {
10156         /**
10157          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10158          * origin for the slide effect.  This function automatically handles wrapping the element with
10159          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element in from the top
10163 el.slideIn();
10164
10165 // custom: slide the element in from the right with a 2-second duration
10166 el.slideIn('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideIn('t', {
10170     easing: 'easeOut',
10171     duration: .5
10172 });
10173 </code></pre>
10174          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10175          * @param {Object} options (optional) Object literal with any of the Fx config options
10176          * @return {Roo.Element} The Element
10177          */
10178     slideIn : function(anchor, o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181
10182         el.queueFx(o, function(){
10183
10184             anchor = anchor || "t";
10185
10186             // fix display to visibility
10187             this.fixDisplay();
10188
10189             // restore values after effect
10190             var r = this.getFxRestore();
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "hidden");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             // clear out temp styles after slide and unwrap
10203             var after = function(){
10204                 el.fxUnwrap(wrap, r.pos, o);
10205                 st.width = r.width;
10206                 st.height = r.height;
10207                 el.afterFx(o);
10208             };
10209             // time to calc the positions
10210             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10211
10212             switch(anchor.toLowerCase()){
10213                 case "t":
10214                     wrap.setSize(b.width, 0);
10215                     st.left = st.bottom = "0";
10216                     a = {height: bh};
10217                 break;
10218                 case "l":
10219                     wrap.setSize(0, b.height);
10220                     st.right = st.top = "0";
10221                     a = {width: bw};
10222                 break;
10223                 case "r":
10224                     wrap.setSize(0, b.height);
10225                     wrap.setX(b.right);
10226                     st.left = st.top = "0";
10227                     a = {width: bw, points: pt};
10228                 break;
10229                 case "b":
10230                     wrap.setSize(b.width, 0);
10231                     wrap.setY(b.bottom);
10232                     st.left = st.top = "0";
10233                     a = {height: bh, points: pt};
10234                 break;
10235                 case "tl":
10236                     wrap.setSize(0, 0);
10237                     st.right = st.bottom = "0";
10238                     a = {width: bw, height: bh};
10239                 break;
10240                 case "bl":
10241                     wrap.setSize(0, 0);
10242                     wrap.setY(b.y+b.height);
10243                     st.right = st.top = "0";
10244                     a = {width: bw, height: bh, points: pt};
10245                 break;
10246                 case "br":
10247                     wrap.setSize(0, 0);
10248                     wrap.setXY([b.right, b.bottom]);
10249                     st.left = st.top = "0";
10250                     a = {width: bw, height: bh, points: pt};
10251                 break;
10252                 case "tr":
10253                     wrap.setSize(0, 0);
10254                     wrap.setX(b.x+b.width);
10255                     st.left = st.bottom = "0";
10256                     a = {width: bw, height: bh, points: pt};
10257                 break;
10258             }
10259             this.dom.style.visibility = "visible";
10260             wrap.show();
10261
10262             arguments.callee.anim = wrap.fxanim(a,
10263                 o,
10264                 'motion',
10265                 .5,
10266                 'easeOut', after);
10267         });
10268         return this;
10269     },
10270     
10271         /**
10272          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10273          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10274          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10275          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10276          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10277          * Usage:
10278          *<pre><code>
10279 // default: slide the element out to the top
10280 el.slideOut();
10281
10282 // custom: slide the element out to the right with a 2-second duration
10283 el.slideOut('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideOut('t', {
10287     easing: 'easeOut',
10288     duration: .5,
10289     remove: false,
10290     useDisplay: false
10291 });
10292 </code></pre>
10293          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     slideOut : function(anchor, o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302
10303             anchor = anchor || "t";
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             
10308             var b = this.getBox();
10309             // fixed size for slide
10310             this.setSize(b);
10311
10312             // wrap if needed
10313             var wrap = this.fxWrap(r.pos, o, "visible");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             wrap.setSize(b);
10320
10321             var after = function(){
10322                 if(o.useDisplay){
10323                     el.setDisplayed(false);
10324                 }else{
10325                     el.hide();
10326                 }
10327
10328                 el.fxUnwrap(wrap, r.pos, o);
10329
10330                 st.width = r.width;
10331                 st.height = r.height;
10332
10333                 el.afterFx(o);
10334             };
10335
10336             var a, zero = {to: 0};
10337             switch(anchor.toLowerCase()){
10338                 case "t":
10339                     st.left = st.bottom = "0";
10340                     a = {height: zero};
10341                 break;
10342                 case "l":
10343                     st.right = st.top = "0";
10344                     a = {width: zero};
10345                 break;
10346                 case "r":
10347                     st.left = st.top = "0";
10348                     a = {width: zero, points: {to:[b.right, b.y]}};
10349                 break;
10350                 case "b":
10351                     st.left = st.top = "0";
10352                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10353                 break;
10354                 case "tl":
10355                     st.right = st.bottom = "0";
10356                     a = {width: zero, height: zero};
10357                 break;
10358                 case "bl":
10359                     st.right = st.top = "0";
10360                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10361                 break;
10362                 case "br":
10363                     st.left = st.top = "0";
10364                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10365                 break;
10366                 case "tr":
10367                     st.left = st.bottom = "0";
10368                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10369                 break;
10370             }
10371
10372             arguments.callee.anim = wrap.fxanim(a,
10373                 o,
10374                 'motion',
10375                 .5,
10376                 "easeOut", after);
10377         });
10378         return this;
10379     },
10380
10381         /**
10382          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10383          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10384          * The element must be removed from the DOM using the 'remove' config option if desired.
10385          * Usage:
10386          *<pre><code>
10387 // default
10388 el.puff();
10389
10390 // common config options shown with default values
10391 el.puff({
10392     easing: 'easeOut',
10393     duration: .5,
10394     remove: false,
10395     useDisplay: false
10396 });
10397 </code></pre>
10398          * @param {Object} options (optional) Object literal with any of the Fx config options
10399          * @return {Roo.Element} The Element
10400          */
10401     puff : function(o){
10402         var el = this.getFxEl();
10403         o = o || {};
10404
10405         el.queueFx(o, function(){
10406             this.clearOpacity();
10407             this.show();
10408
10409             // restore values after effect
10410             var r = this.getFxRestore();
10411             var st = this.dom.style;
10412
10413             var after = function(){
10414                 if(o.useDisplay){
10415                     el.setDisplayed(false);
10416                 }else{
10417                     el.hide();
10418                 }
10419
10420                 el.clearOpacity();
10421
10422                 el.setPositioning(r.pos);
10423                 st.width = r.width;
10424                 st.height = r.height;
10425                 st.fontSize = '';
10426                 el.afterFx(o);
10427             };
10428
10429             var width = this.getWidth();
10430             var height = this.getHeight();
10431
10432             arguments.callee.anim = this.fxanim({
10433                     width : {to: this.adjustWidth(width * 2)},
10434                     height : {to: this.adjustHeight(height * 2)},
10435                     points : {by: [-(width * .5), -(height * .5)]},
10436                     opacity : {to: 0},
10437                     fontSize: {to:200, unit: "%"}
10438                 },
10439                 o,
10440                 'motion',
10441                 .5,
10442                 "easeOut", after);
10443         });
10444         return this;
10445     },
10446
10447         /**
10448          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10449          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10450          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10451          * Usage:
10452          *<pre><code>
10453 // default
10454 el.switchOff();
10455
10456 // all config options shown with default values
10457 el.switchOff({
10458     easing: 'easeIn',
10459     duration: .3,
10460     remove: false,
10461     useDisplay: false
10462 });
10463 </code></pre>
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     switchOff : function(o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             this.clearOpacity();
10473             this.clip();
10474
10475             // restore values after effect
10476             var r = this.getFxRestore();
10477             var st = this.dom.style;
10478
10479             var after = function(){
10480                 if(o.useDisplay){
10481                     el.setDisplayed(false);
10482                 }else{
10483                     el.hide();
10484                 }
10485
10486                 el.clearOpacity();
10487                 el.setPositioning(r.pos);
10488                 st.width = r.width;
10489                 st.height = r.height;
10490
10491                 el.afterFx(o);
10492             };
10493
10494             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10495                 this.clearOpacity();
10496                 (function(){
10497                     this.fxanim({
10498                         height:{to:1},
10499                         points:{by:[0, this.getHeight() * .5]}
10500                     }, o, 'motion', 0.3, 'easeIn', after);
10501                 }).defer(100, this);
10502             });
10503         });
10504         return this;
10505     },
10506
10507     /**
10508      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10509      * changed using the "attr" config option) and then fading back to the original color. If no original
10510      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10511      * Usage:
10512 <pre><code>
10513 // default: highlight background to yellow
10514 el.highlight();
10515
10516 // custom: highlight foreground text to blue for 2 seconds
10517 el.highlight("0000ff", { attr: 'color', duration: 2 });
10518
10519 // common config options shown with default values
10520 el.highlight("ffff9c", {
10521     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10522     endColor: (current color) or "ffffff",
10523     easing: 'easeIn',
10524     duration: 1
10525 });
10526 </code></pre>
10527      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10528      * @param {Object} options (optional) Object literal with any of the Fx config options
10529      * @return {Roo.Element} The Element
10530      */ 
10531     highlight : function(color, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "ffff9c";
10537             attr = o.attr || "backgroundColor";
10538
10539             this.clearOpacity();
10540             this.show();
10541
10542             var origColor = this.getColor(attr);
10543             var restoreColor = this.dom.style[attr];
10544             endColor = (o.endColor || origColor) || "ffffff";
10545
10546             var after = function(){
10547                 el.dom.style[attr] = restoreColor;
10548                 el.afterFx(o);
10549             };
10550
10551             var a = {};
10552             a[attr] = {from: color, to: endColor};
10553             arguments.callee.anim = this.fxanim(a,
10554                 o,
10555                 'color',
10556                 1,
10557                 'easeIn', after);
10558         });
10559         return this;
10560     },
10561
10562    /**
10563     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10564     * Usage:
10565 <pre><code>
10566 // default: a single light blue ripple
10567 el.frame();
10568
10569 // custom: 3 red ripples lasting 3 seconds total
10570 el.frame("ff0000", 3, { duration: 3 });
10571
10572 // common config options shown with default values
10573 el.frame("C3DAF9", 1, {
10574     duration: 1 //duration of entire animation (not each individual ripple)
10575     // Note: Easing is not configurable and will be ignored if included
10576 });
10577 </code></pre>
10578     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10579     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     frame : function(color, count, o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586
10587         el.queueFx(o, function(){
10588             color = color || "#C3DAF9";
10589             if(color.length == 6){
10590                 color = "#" + color;
10591             }
10592             count = count || 1;
10593             duration = o.duration || 1;
10594             this.show();
10595
10596             var b = this.getBox();
10597             var animFn = function(){
10598                 var proxy = this.createProxy({
10599
10600                      style:{
10601                         visbility:"hidden",
10602                         position:"absolute",
10603                         "z-index":"35000", // yee haw
10604                         border:"0px solid " + color
10605                      }
10606                   });
10607                 var scale = Roo.isBorderBox ? 2 : 1;
10608                 proxy.animate({
10609                     top:{from:b.y, to:b.y - 20},
10610                     left:{from:b.x, to:b.x - 20},
10611                     borderWidth:{from:0, to:10},
10612                     opacity:{from:1, to:0},
10613                     height:{from:b.height, to:(b.height + (20*scale))},
10614                     width:{from:b.width, to:(b.width + (20*scale))}
10615                 }, duration, function(){
10616                     proxy.remove();
10617                 });
10618                 if(--count > 0){
10619                      animFn.defer((duration/2)*1000, this);
10620                 }else{
10621                     el.afterFx(o);
10622                 }
10623             };
10624             animFn.call(this);
10625         });
10626         return this;
10627     },
10628
10629    /**
10630     * Creates a pause before any subsequent queued effects begin.  If there are
10631     * no effects queued after the pause it will have no effect.
10632     * Usage:
10633 <pre><code>
10634 el.pause(1);
10635 </code></pre>
10636     * @param {Number} seconds The length of time to pause (in seconds)
10637     * @return {Roo.Element} The Element
10638     */
10639     pause : function(seconds){
10640         var el = this.getFxEl();
10641         var o = {};
10642
10643         el.queueFx(o, function(){
10644             setTimeout(function(){
10645                 el.afterFx(o);
10646             }, seconds * 1000);
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade in from opacity 0 to 100%
10657 el.fadeIn();
10658
10659 // custom: fade in from opacity 0 to 75% over 2 seconds
10660 el.fadeIn({ endOpacity: .75, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeIn({
10664     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667 });
10668 </code></pre>
10669     * @param {Object} options (optional) Object literal with any of the Fx config options
10670     * @return {Roo.Element} The Element
10671     */
10672     fadeIn : function(o){
10673         var el = this.getFxEl();
10674         o = o || {};
10675         el.queueFx(o, function(){
10676             this.setOpacity(0);
10677             this.fixDisplay();
10678             this.dom.style.visibility = 'visible';
10679             var to = o.endOpacity || 1;
10680             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10681                 o, null, .5, "easeOut", function(){
10682                 if(to == 1){
10683                     this.clearOpacity();
10684                 }
10685                 el.afterFx(o);
10686             });
10687         });
10688         return this;
10689     },
10690
10691    /**
10692     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10693     * using the "endOpacity" config option.
10694     * Usage:
10695 <pre><code>
10696 // default: fade out from the element's current opacity to 0
10697 el.fadeOut();
10698
10699 // custom: fade out from the element's current opacity to 25% over 2 seconds
10700 el.fadeOut({ endOpacity: .25, duration: 2});
10701
10702 // common config options shown with default values
10703 el.fadeOut({
10704     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10705     easing: 'easeOut',
10706     duration: .5
10707     remove: false,
10708     useDisplay: false
10709 });
10710 </code></pre>
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     fadeOut : function(o){
10715         var el = this.getFxEl();
10716         o = o || {};
10717         el.queueFx(o, function(){
10718             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10721                      this.dom.style.display = "none";
10722                 }else{
10723                      this.dom.style.visibility = "hidden";
10724                 }
10725                 this.clearOpacity();
10726                 el.afterFx(o);
10727             });
10728         });
10729         return this;
10730     },
10731
10732    /**
10733     * Animates the transition of an element's dimensions from a starting height/width
10734     * to an ending height/width.
10735     * Usage:
10736 <pre><code>
10737 // change height and width to 100x100 pixels
10738 el.scale(100, 100);
10739
10740 // common config options shown with default values.  The height and width will default to
10741 // the element's existing values if passed as null.
10742 el.scale(
10743     [element's width],
10744     [element's height], {
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Number} width  The new width (pass undefined to keep the original width)
10750     * @param {Number} height  The new height (pass undefined to keep the original height)
10751     * @param {Object} options (optional) Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     scale : function(w, h, o){
10755         this.shift(Roo.apply({}, o, {
10756             width: w,
10757             height: h
10758         }));
10759         return this;
10760     },
10761
10762    /**
10763     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10764     * Any of these properties not specified in the config object will not be changed.  This effect 
10765     * requires that at least one new dimension, position or opacity setting must be passed in on
10766     * the config object in order for the function to have any effect.
10767     * Usage:
10768 <pre><code>
10769 // slide the element horizontally to x position 200 while changing the height and opacity
10770 el.shift({ x: 200, height: 50, opacity: .8 });
10771
10772 // common config options shown with default values.
10773 el.shift({
10774     width: [element's width],
10775     height: [element's height],
10776     x: [element's x position],
10777     y: [element's y position],
10778     opacity: [element's opacity],
10779     easing: 'easeOut',
10780     duration: .35
10781 });
10782 </code></pre>
10783     * @param {Object} options  Object literal with any of the Fx config options
10784     * @return {Roo.Element} The Element
10785     */
10786     shift : function(o){
10787         var el = this.getFxEl();
10788         o = o || {};
10789         el.queueFx(o, function(){
10790             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10791             if(w !== undefined){
10792                 a.width = {to: this.adjustWidth(w)};
10793             }
10794             if(h !== undefined){
10795                 a.height = {to: this.adjustHeight(h)};
10796             }
10797             if(x !== undefined || y !== undefined){
10798                 a.points = {to: [
10799                     x !== undefined ? x : this.getX(),
10800                     y !== undefined ? y : this.getY()
10801                 ]};
10802             }
10803             if(op !== undefined){
10804                 a.opacity = {to: op};
10805             }
10806             if(o.xy !== undefined){
10807                 a.points = {to: o.xy};
10808             }
10809             arguments.callee.anim = this.fxanim(a,
10810                 o, 'motion', .35, "easeOut", function(){
10811                 el.afterFx(o);
10812             });
10813         });
10814         return this;
10815     },
10816
10817         /**
10818          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10819          * ending point of the effect.
10820          * Usage:
10821          *<pre><code>
10822 // default: slide the element downward while fading out
10823 el.ghost();
10824
10825 // custom: slide the element out to the right with a 2-second duration
10826 el.ghost('r', { duration: 2 });
10827
10828 // common config options shown with default values
10829 el.ghost('b', {
10830     easing: 'easeOut',
10831     duration: .5
10832     remove: false,
10833     useDisplay: false
10834 });
10835 </code></pre>
10836          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10837          * @param {Object} options (optional) Object literal with any of the Fx config options
10838          * @return {Roo.Element} The Element
10839          */
10840     ghost : function(anchor, o){
10841         var el = this.getFxEl();
10842         o = o || {};
10843
10844         el.queueFx(o, function(){
10845             anchor = anchor || "b";
10846
10847             // restore values after effect
10848             var r = this.getFxRestore();
10849             var w = this.getWidth(),
10850                 h = this.getHeight();
10851
10852             var st = this.dom.style;
10853
10854             var after = function(){
10855                 if(o.useDisplay){
10856                     el.setDisplayed(false);
10857                 }else{
10858                     el.hide();
10859                 }
10860
10861                 el.clearOpacity();
10862                 el.setPositioning(r.pos);
10863                 st.width = r.width;
10864                 st.height = r.height;
10865
10866                 el.afterFx(o);
10867             };
10868
10869             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10870             switch(anchor.toLowerCase()){
10871                 case "t":
10872                     pt.by = [0, -h];
10873                 break;
10874                 case "l":
10875                     pt.by = [-w, 0];
10876                 break;
10877                 case "r":
10878                     pt.by = [w, 0];
10879                 break;
10880                 case "b":
10881                     pt.by = [0, h];
10882                 break;
10883                 case "tl":
10884                     pt.by = [-w, -h];
10885                 break;
10886                 case "bl":
10887                     pt.by = [-w, h];
10888                 break;
10889                 case "br":
10890                     pt.by = [w, h];
10891                 break;
10892                 case "tr":
10893                     pt.by = [w, -h];
10894                 break;
10895             }
10896
10897             arguments.callee.anim = this.fxanim(a,
10898                 o,
10899                 'motion',
10900                 .5,
10901                 "easeOut", after);
10902         });
10903         return this;
10904     },
10905
10906         /**
10907          * Ensures that all effects queued after syncFx is called on the element are
10908          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10909          * @return {Roo.Element} The Element
10910          */
10911     syncFx : function(){
10912         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10913             block : false,
10914             concurrent : true,
10915             stopFx : false
10916         });
10917         return this;
10918     },
10919
10920         /**
10921          * Ensures that all effects queued after sequenceFx is called on the element are
10922          * run in sequence.  This is the opposite of {@link #syncFx}.
10923          * @return {Roo.Element} The Element
10924          */
10925     sequenceFx : function(){
10926         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10927             block : false,
10928             concurrent : false,
10929             stopFx : false
10930         });
10931         return this;
10932     },
10933
10934         /* @private */
10935     nextFx : function(){
10936         var ef = this.fxQueue[0];
10937         if(ef){
10938             ef.call(this);
10939         }
10940     },
10941
10942         /**
10943          * Returns true if the element has any effects actively running or queued, else returns false.
10944          * @return {Boolean} True if element has active effects, else false
10945          */
10946     hasActiveFx : function(){
10947         return this.fxQueue && this.fxQueue[0];
10948     },
10949
10950         /**
10951          * Stops any running effects and clears the element's internal effects queue if it contains
10952          * any additional effects that haven't started yet.
10953          * @return {Roo.Element} The Element
10954          */
10955     stopFx : function(){
10956         if(this.hasActiveFx()){
10957             var cur = this.fxQueue[0];
10958             if(cur && cur.anim && cur.anim.isAnimated()){
10959                 this.fxQueue = [cur]; // clear out others
10960                 cur.anim.stop(true);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     beforeFx : function(o){
10968         if(this.hasActiveFx() && !o.concurrent){
10969            if(o.stopFx){
10970                this.stopFx();
10971                return true;
10972            }
10973            return false;
10974         }
10975         return true;
10976     },
10977
10978         /**
10979          * Returns true if the element is currently blocking so that no other effect can be queued
10980          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10981          * used to ensure that an effect initiated by a user action runs to completion prior to the
10982          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10983          * @return {Boolean} True if blocking, else false
10984          */
10985     hasFxBlock : function(){
10986         var q = this.fxQueue;
10987         return q && q[0] && q[0].block;
10988     },
10989
10990         /* @private */
10991     queueFx : function(o, fn){
10992         if(!this.fxQueue){
10993             this.fxQueue = [];
10994         }
10995         if(!this.hasFxBlock()){
10996             Roo.applyIf(o, this.fxDefaults);
10997             if(!o.concurrent){
10998                 var run = this.beforeFx(o);
10999                 fn.block = o.block;
11000                 this.fxQueue.push(fn);
11001                 if(run){
11002                     this.nextFx();
11003                 }
11004             }else{
11005                 fn.call(this);
11006             }
11007         }
11008         return this;
11009     },
11010
11011         /* @private */
11012     fxWrap : function(pos, o, vis){
11013         var wrap;
11014         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11015             var wrapXY;
11016             if(o.fixPosition){
11017                 wrapXY = this.getXY();
11018             }
11019             var div = document.createElement("div");
11020             div.style.visibility = vis;
11021             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11022             wrap.setPositioning(pos);
11023             if(wrap.getStyle("position") == "static"){
11024                 wrap.position("relative");
11025             }
11026             this.clearPositioning('auto');
11027             wrap.clip();
11028             wrap.dom.appendChild(this.dom);
11029             if(wrapXY){
11030                 wrap.setXY(wrapXY);
11031             }
11032         }
11033         return wrap;
11034     },
11035
11036         /* @private */
11037     fxUnwrap : function(wrap, pos, o){
11038         this.clearPositioning();
11039         this.setPositioning(pos);
11040         if(!o.wrap){
11041             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11042             wrap.remove();
11043         }
11044     },
11045
11046         /* @private */
11047     getFxRestore : function(){
11048         var st = this.dom.style;
11049         return {pos: this.getPositioning(), width: st.width, height : st.height};
11050     },
11051
11052         /* @private */
11053     afterFx : function(o){
11054         if(o.afterStyle){
11055             this.applyStyles(o.afterStyle);
11056         }
11057         if(o.afterCls){
11058             this.addClass(o.afterCls);
11059         }
11060         if(o.remove === true){
11061             this.remove();
11062         }
11063         Roo.callback(o.callback, o.scope, [this]);
11064         if(!o.concurrent){
11065             this.fxQueue.shift();
11066             this.nextFx();
11067         }
11068     },
11069
11070         /* @private */
11071     getFxEl : function(){ // support for composite element fx
11072         return Roo.get(this.dom);
11073     },
11074
11075         /* @private */
11076     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11077         animType = animType || 'run';
11078         opt = opt || {};
11079         var anim = Roo.lib.Anim[animType](
11080             this.dom, args,
11081             (opt.duration || defaultDur) || .35,
11082             (opt.easing || defaultEase) || 'easeOut',
11083             function(){
11084                 Roo.callback(cb, this);
11085             },
11086             this
11087         );
11088         opt.anim = anim;
11089         return anim;
11090     }
11091 };
11092
11093 // backwords compat
11094 Roo.Fx.resize = Roo.Fx.scale;
11095
11096 //When included, Roo.Fx is automatically applied to Element so that all basic
11097 //effects are available directly via the Element API
11098 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11099  * Based on:
11100  * Ext JS Library 1.1.1
11101  * Copyright(c) 2006-2007, Ext JS, LLC.
11102  *
11103  * Originally Released Under LGPL - original licence link has changed is not relivant.
11104  *
11105  * Fork - LGPL
11106  * <script type="text/javascript">
11107  */
11108
11109
11110 /**
11111  * @class Roo.CompositeElement
11112  * Standard composite class. Creates a Roo.Element for every element in the collection.
11113  * <br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  * <br><br>
11117  * All methods return <i>this</i> and can be chained.
11118  <pre><code>
11119  var els = Roo.select("#some-el div.some-class", true);
11120  // or select directly from an existing element
11121  var el = Roo.get('some-el');
11122  el.select('div.some-class', true);
11123
11124  els.setWidth(100); // all elements become 100 width
11125  els.hide(true); // all elements fade out and hide
11126  // or
11127  els.setWidth(100).hide(true);
11128  </code></pre>
11129  */
11130 Roo.CompositeElement = function(els){
11131     this.elements = [];
11132     this.addElements(els);
11133 };
11134 Roo.CompositeElement.prototype = {
11135     isComposite: true,
11136     addElements : function(els){
11137         if(!els) {
11138             return this;
11139         }
11140         if(typeof els == "string"){
11141             els = Roo.Element.selectorFunction(els);
11142         }
11143         var yels = this.elements;
11144         var index = yels.length-1;
11145         for(var i = 0, len = els.length; i < len; i++) {
11146                 yels[++index] = Roo.get(els[i]);
11147         }
11148         return this;
11149     },
11150
11151     /**
11152     * Clears this composite and adds the elements returned by the passed selector.
11153     * @param {String/Array} els A string CSS selector, an array of elements or an element
11154     * @return {CompositeElement} this
11155     */
11156     fill : function(els){
11157         this.elements = [];
11158         this.add(els);
11159         return this;
11160     },
11161
11162     /**
11163     * Filters this composite to only elements that match the passed selector.
11164     * @param {String} selector A string CSS selector
11165     * @param {Boolean} inverse return inverse filter (not matches)
11166     * @return {CompositeElement} this
11167     */
11168     filter : function(selector, inverse){
11169         var els = [];
11170         inverse = inverse || false;
11171         this.each(function(el){
11172             var match = inverse ? !el.is(selector) : el.is(selector);
11173             if(match){
11174                 els[els.length] = el.dom;
11175             }
11176         });
11177         this.fill(els);
11178         return this;
11179     },
11180
11181     invoke : function(fn, args){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 Roo.Element.prototype[fn].apply(els[i], args);
11185         }
11186         return this;
11187     },
11188     /**
11189     * Adds elements to this composite.
11190     * @param {String/Array} els A string CSS selector, an array of elements or an element
11191     * @return {CompositeElement} this
11192     */
11193     add : function(els){
11194         if(typeof els == "string"){
11195             this.addElements(Roo.Element.selectorFunction(els));
11196         }else if(els.length !== undefined){
11197             this.addElements(els);
11198         }else{
11199             this.addElements([els]);
11200         }
11201         return this;
11202     },
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite.
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         for(var i = 0, len = els.length; i < len; i++){
11212             if(fn.call(scope || els[i], els[i], this, i) === false) {
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     /**
11220      * Returns the Element object at the specified index
11221      * @param {Number} index
11222      * @return {Roo.Element}
11223      */
11224     item : function(index){
11225         return this.elements[index] || null;
11226     },
11227
11228     /**
11229      * Returns the first Element
11230      * @return {Roo.Element}
11231      */
11232     first : function(){
11233         return this.item(0);
11234     },
11235
11236     /**
11237      * Returns the last Element
11238      * @return {Roo.Element}
11239      */
11240     last : function(){
11241         return this.item(this.elements.length-1);
11242     },
11243
11244     /**
11245      * Returns the number of elements in this composite
11246      * @return Number
11247      */
11248     getCount : function(){
11249         return this.elements.length;
11250     },
11251
11252     /**
11253      * Returns true if this composite contains the passed element
11254      * @return Boolean
11255      */
11256     contains : function(el){
11257         return this.indexOf(el) !== -1;
11258     },
11259
11260     /**
11261      * Returns true if this composite contains the passed element
11262      * @return Boolean
11263      */
11264     indexOf : function(el){
11265         return this.elements.indexOf(Roo.get(el));
11266     },
11267
11268
11269     /**
11270     * Removes the specified element(s).
11271     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11272     * or an array of any of those.
11273     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11274     * @return {CompositeElement} this
11275     */
11276     removeElement : function(el, removeDom){
11277         if(el instanceof Array){
11278             for(var i = 0, len = el.length; i < len; i++){
11279                 this.removeElement(el[i]);
11280             }
11281             return this;
11282         }
11283         var index = typeof el == 'number' ? el : this.indexOf(el);
11284         if(index !== -1){
11285             if(removeDom){
11286                 var d = this.elements[index];
11287                 if(d.dom){
11288                     d.remove();
11289                 }else{
11290                     d.parentNode.removeChild(d);
11291                 }
11292             }
11293             this.elements.splice(index, 1);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Replaces the specified element with the passed element.
11300     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11301     * to replace.
11302     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11303     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11304     * @return {CompositeElement} this
11305     */
11306     replaceElement : function(el, replacement, domReplace){
11307         var index = typeof el == 'number' ? el : this.indexOf(el);
11308         if(index !== -1){
11309             if(domReplace){
11310                 this.elements[index].replaceWith(replacement);
11311             }else{
11312                 this.elements.splice(index, 1, Roo.get(replacement))
11313             }
11314         }
11315         return this;
11316     },
11317
11318     /**
11319      * Removes all elements.
11320      */
11321     clear : function(){
11322         this.elements = [];
11323     }
11324 };
11325 (function(){
11326     Roo.CompositeElement.createCall = function(proto, fnName){
11327         if(!proto[fnName]){
11328             proto[fnName] = function(){
11329                 return this.invoke(fnName, arguments);
11330             };
11331         }
11332     };
11333     for(var fnName in Roo.Element.prototype){
11334         if(typeof Roo.Element.prototype[fnName] == "function"){
11335             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11336         }
11337     };
11338 })();
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350 /**
11351  * @class Roo.CompositeElementLite
11352  * @extends Roo.CompositeElement
11353  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11354  <pre><code>
11355  var els = Roo.select("#some-el div.some-class");
11356  // or select directly from an existing element
11357  var el = Roo.get('some-el');
11358  el.select('div.some-class');
11359
11360  els.setWidth(100); // all elements become 100 width
11361  els.hide(true); // all elements fade out and hide
11362  // or
11363  els.setWidth(100).hide(true);
11364  </code></pre><br><br>
11365  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11366  * actions will be performed on all the elements in this collection.</b>
11367  */
11368 Roo.CompositeElementLite = function(els){
11369     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11370     this.el = new Roo.Element.Flyweight();
11371 };
11372 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11373     addElements : function(els){
11374         if(els){
11375             if(els instanceof Array){
11376                 this.elements = this.elements.concat(els);
11377             }else{
11378                 var yels = this.elements;
11379                 var index = yels.length-1;
11380                 for(var i = 0, len = els.length; i < len; i++) {
11381                     yels[++index] = els[i];
11382                 }
11383             }
11384         }
11385         return this;
11386     },
11387     invoke : function(fn, args){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++) {
11391             el.dom = els[i];
11392                 Roo.Element.prototype[fn].apply(el, args);
11393         }
11394         return this;
11395     },
11396     /**
11397      * Returns a flyweight Element of the dom element object at the specified index
11398      * @param {Number} index
11399      * @return {Roo.Element}
11400      */
11401     item : function(index){
11402         if(!this.elements[index]){
11403             return null;
11404         }
11405         this.el.dom = this.elements[index];
11406         return this.el;
11407     },
11408
11409     // fixes scope with flyweight
11410     addListener : function(eventName, handler, scope, opt){
11411         var els = this.elements;
11412         for(var i = 0, len = els.length; i < len; i++) {
11413             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11420     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11421     * a reference to the dom node, use el.dom.</b>
11422     * @param {Function} fn The function to call
11423     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11424     * @return {CompositeElement} this
11425     */
11426     each : function(fn, scope){
11427         var els = this.elements;
11428         var el = this.el;
11429         for(var i = 0, len = els.length; i < len; i++){
11430             el.dom = els[i];
11431                 if(fn.call(scope || el, el, this, i) === false){
11432                 break;
11433             }
11434         }
11435         return this;
11436     },
11437
11438     indexOf : function(el){
11439         return this.elements.indexOf(Roo.getDom(el));
11440     },
11441
11442     replaceElement : function(el, replacement, domReplace){
11443         var index = typeof el == 'number' ? el : this.indexOf(el);
11444         if(index !== -1){
11445             replacement = Roo.getDom(replacement);
11446             if(domReplace){
11447                 var d = this.elements[index];
11448                 d.parentNode.insertBefore(replacement, d);
11449                 d.parentNode.removeChild(d);
11450             }
11451             this.elements.splice(index, 1, replacement);
11452         }
11453         return this;
11454     }
11455 });
11456 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11457
11458 /*
11459  * Based on:
11460  * Ext JS Library 1.1.1
11461  * Copyright(c) 2006-2007, Ext JS, LLC.
11462  *
11463  * Originally Released Under LGPL - original licence link has changed is not relivant.
11464  *
11465  * Fork - LGPL
11466  * <script type="text/javascript">
11467  */
11468
11469  
11470
11471 /**
11472  * @class Roo.data.Connection
11473  * @extends Roo.util.Observable
11474  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11475  * either to a configured URL, or to a URL specified at request time. 
11476  * 
11477  * Requests made by this class are asynchronous, and will return immediately. No data from
11478  * the server will be available to the statement immediately following the {@link #request} call.
11479  * To process returned data, use a callback in the request options object, or an event listener.
11480  * 
11481  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11482  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11483  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11484  * property and, if present, the IFRAME's XML document as the responseXML property.
11485  * 
11486  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11487  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11488  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11489  * standard DOM methods.
11490  * @constructor
11491  * @param {Object} config a configuration object.
11492  */
11493 Roo.data.Connection = function(config){
11494     Roo.apply(this, config);
11495     this.addEvents({
11496         /**
11497          * @event beforerequest
11498          * Fires before a network request is made to retrieve a data object.
11499          * @param {Connection} conn This Connection object.
11500          * @param {Object} options The options config object passed to the {@link #request} method.
11501          */
11502         "beforerequest" : true,
11503         /**
11504          * @event requestcomplete
11505          * Fires if the request was successfully completed.
11506          * @param {Connection} conn This Connection object.
11507          * @param {Object} response The XHR object containing the response data.
11508          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11509          * @param {Object} options The options config object passed to the {@link #request} method.
11510          */
11511         "requestcomplete" : true,
11512         /**
11513          * @event requestexception
11514          * Fires if an error HTTP status was returned from the server.
11515          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11516          * @param {Connection} conn This Connection object.
11517          * @param {Object} response The XHR object containing the response data.
11518          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11519          * @param {Object} options The options config object passed to the {@link #request} method.
11520          */
11521         "requestexception" : true
11522     });
11523     Roo.data.Connection.superclass.constructor.call(this);
11524 };
11525
11526 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11527     /**
11528      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11529      */
11530     /**
11531      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11536      *  to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11540      */
11541     /**
11542      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      */
11544     timeout : 30000,
11545     /**
11546      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11547      * @type Boolean
11548      */
11549     autoAbort:false,
11550
11551     /**
11552      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11553      * @type Boolean
11554      */
11555     disableCaching: true,
11556
11557     /**
11558      * Sends an HTTP request to a remote server.
11559      * @param {Object} options An object which may contain the following properties:<ul>
11560      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11561      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11562      * request, a url encoded string or a function to call to get either.</li>
11563      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11564      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11565      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11566      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11567      * <li>options {Object} The parameter to the request call.</li>
11568      * <li>success {Boolean} True if the request succeeded.</li>
11569      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11570      * </ul></li>
11571      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11572      * The callback is passed the following parameters:<ul>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * <li>options {Object} The parameter to the request call.</li>
11575      * </ul></li>
11576      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11577      * The callback is passed the following parameters:<ul>
11578      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11579      * <li>options {Object} The parameter to the request call.</li>
11580      * </ul></li>
11581      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11582      * for the callback function. Defaults to the browser window.</li>
11583      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11584      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11585      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11586      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11587      * params for the post data. Any params will be appended to the URL.</li>
11588      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11589      * </ul>
11590      * @return {Number} transactionId
11591      */
11592     request : function(o){
11593         if(this.fireEvent("beforerequest", this, o) !== false){
11594             var p = o.params;
11595
11596             if(typeof p == "function"){
11597                 p = p.call(o.scope||window, o);
11598             }
11599             if(typeof p == "object"){
11600                 p = Roo.urlEncode(o.params);
11601             }
11602             if(this.extraParams){
11603                 var extras = Roo.urlEncode(this.extraParams);
11604                 p = p ? (p + '&' + extras) : extras;
11605             }
11606
11607             var url = o.url || this.url;
11608             if(typeof url == 'function'){
11609                 url = url.call(o.scope||window, o);
11610             }
11611
11612             if(o.form){
11613                 var form = Roo.getDom(o.form);
11614                 url = url || form.action;
11615
11616                 var enctype = form.getAttribute("enctype");
11617                 
11618                 if (o.formData) {
11619                     return this.doFormDataUpload(o,p,url);
11620                 }
11621                 
11622                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11623                     return this.doFormUpload(o, p, url);
11624                 }
11625                 var f = Roo.lib.Ajax.serializeForm(form);
11626                 p = p ? (p + '&' + f) : f;
11627             }
11628
11629             var hs = o.headers;
11630             if(this.defaultHeaders){
11631                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11632                 if(!o.headers){
11633                     o.headers = hs;
11634                 }
11635             }
11636
11637             var cb = {
11638                 success: this.handleResponse,
11639                 failure: this.handleFailure,
11640                 scope: this,
11641                 argument: {options: o},
11642                 timeout : o.timeout || this.timeout
11643             };
11644
11645             var method = o.method||this.method||(p ? "POST" : "GET");
11646
11647             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11648                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11649             }
11650
11651             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11652                 if(o.autoAbort){
11653                     this.abort();
11654                 }
11655             }else if(this.autoAbort !== false){
11656                 this.abort();
11657             }
11658
11659             if((method == 'GET' && p) || o.xmlData){
11660                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11661                 p = '';
11662             }
11663             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11664             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11665             Roo.lib.Ajax.useDefaultHeader == true;
11666             return this.transId;
11667         }else{
11668             Roo.callback(o.callback, o.scope, [o, null, null]);
11669             return null;
11670         }
11671     },
11672
11673     /**
11674      * Determine whether this object has a request outstanding.
11675      * @param {Number} transactionId (Optional) defaults to the last transaction
11676      * @return {Boolean} True if there is an outstanding request.
11677      */
11678     isLoading : function(transId){
11679         if(transId){
11680             return Roo.lib.Ajax.isCallInProgress(transId);
11681         }else{
11682             return this.transId ? true : false;
11683         }
11684     },
11685
11686     /**
11687      * Aborts any outstanding request.
11688      * @param {Number} transactionId (Optional) defaults to the last transaction
11689      */
11690     abort : function(transId){
11691         if(transId || this.isLoading()){
11692             Roo.lib.Ajax.abort(transId || this.transId);
11693         }
11694     },
11695
11696     // private
11697     handleResponse : function(response){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestcomplete", this, response, options);
11702         Roo.callback(options.success, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, true, response]);
11704     },
11705
11706     // private
11707     handleFailure : function(response, e){
11708         this.transId = false;
11709         var options = response.argument.options;
11710         response.argument = options ? options.argument : null;
11711         this.fireEvent("requestexception", this, response, options, e);
11712         Roo.callback(options.failure, options.scope, [response, options]);
11713         Roo.callback(options.callback, options.scope, [options, false, response]);
11714     },
11715
11716     // private
11717     doFormUpload : function(o, ps, url){
11718         var id = Roo.id();
11719         var frame = document.createElement('iframe');
11720         frame.id = id;
11721         frame.name = id;
11722         frame.className = 'x-hidden';
11723         if(Roo.isIE){
11724             frame.src = Roo.SSL_SECURE_URL;
11725         }
11726         document.body.appendChild(frame);
11727
11728         if(Roo.isIE){
11729            document.frames[id].name = id;
11730         }
11731
11732         var form = Roo.getDom(o.form);
11733         form.target = id;
11734         form.method = 'POST';
11735         form.enctype = form.encoding = 'multipart/form-data';
11736         if(url){
11737             form.action = url;
11738         }
11739
11740         var hiddens, hd;
11741         if(ps){ // add dynamic params
11742             hiddens = [];
11743             ps = Roo.urlDecode(ps, false);
11744             for(var k in ps){
11745                 if(ps.hasOwnProperty(k)){
11746                     hd = document.createElement('input');
11747                     hd.type = 'hidden';
11748                     hd.name = k;
11749                     hd.value = ps[k];
11750                     form.appendChild(hd);
11751                     hiddens.push(hd);
11752                 }
11753             }
11754         }
11755
11756         function cb(){
11757             var r = {  // bogus response object
11758                 responseText : '',
11759                 responseXML : null
11760             };
11761
11762             r.argument = o ? o.argument : null;
11763
11764             try { //
11765                 var doc;
11766                 if(Roo.isIE){
11767                     doc = frame.contentWindow.document;
11768                 }else {
11769                     doc = (frame.contentDocument || window.frames[id].document);
11770                 }
11771                 if(doc && doc.body){
11772                     r.responseText = doc.body.innerHTML;
11773                 }
11774                 if(doc && doc.XMLDocument){
11775                     r.responseXML = doc.XMLDocument;
11776                 }else {
11777                     r.responseXML = doc;
11778                 }
11779             }
11780             catch(e) {
11781                 // ignore
11782             }
11783
11784             Roo.EventManager.removeListener(frame, 'load', cb, this);
11785
11786             this.fireEvent("requestcomplete", this, r, o);
11787             Roo.callback(o.success, o.scope, [r, o]);
11788             Roo.callback(o.callback, o.scope, [o, true, r]);
11789
11790             setTimeout(function(){document.body.removeChild(frame);}, 100);
11791         }
11792
11793         Roo.EventManager.on(frame, 'load', cb, this);
11794         form.submit();
11795
11796         if(hiddens){ // remove dynamic params
11797             for(var i = 0, len = hiddens.length; i < len; i++){
11798                 form.removeChild(hiddens[i]);
11799             }
11800         }
11801     },
11802     // this is a 'formdata version???'
11803     
11804     
11805     doFormDataUpload : function(o, ps, url)
11806     {
11807         var form = Roo.getDom(o.form);
11808         form.enctype = form.encoding = 'multipart/form-data';
11809         var formData = o.formData === true ? new FormData(form) : o.formData;
11810       
11811         var cb = {
11812             success: this.handleResponse,
11813             failure: this.handleFailure,
11814             scope: this,
11815             argument: {options: o},
11816             timeout : o.timeout || this.timeout
11817         };
11818  
11819         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11820             if(o.autoAbort){
11821                 this.abort();
11822             }
11823         }else if(this.autoAbort !== false){
11824             this.abort();
11825         }
11826
11827         //Roo.lib.Ajax.defaultPostHeader = null;
11828         Roo.lib.Ajax.useDefaultHeader = false;
11829         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11830         Roo.lib.Ajax.useDefaultHeader = true;
11831  
11832          
11833     }
11834     
11835 });
11836 /*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846  
11847 /**
11848  * Global Ajax request class.
11849  * 
11850  * @class Roo.Ajax
11851  * @extends Roo.data.Connection
11852  * @static
11853  * 
11854  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11855  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11856  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11857  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11858  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11859  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11860  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11861  */
11862 Roo.Ajax = new Roo.data.Connection({
11863     // fix up the docs
11864     /**
11865      * @scope Roo.Ajax
11866      * @type {Boolear} 
11867      */
11868     autoAbort : false,
11869
11870     /**
11871      * Serialize the passed form into a url encoded string
11872      * @scope Roo.Ajax
11873      * @param {String/HTMLElement} form
11874      * @return {String}
11875      */
11876     serializeForm : function(form){
11877         return Roo.lib.Ajax.serializeForm(form);
11878     }
11879 });/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889
11890  
11891 /**
11892  * @class Roo.UpdateManager
11893  * @extends Roo.util.Observable
11894  * Provides AJAX-style update for Element object.<br><br>
11895  * Usage:<br>
11896  * <pre><code>
11897  * // Get it from a Roo.Element object
11898  * var el = Roo.get("foo");
11899  * var mgr = el.getUpdateManager();
11900  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11901  * ...
11902  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11903  * <br>
11904  * // or directly (returns the same UpdateManager instance)
11905  * var mgr = new Roo.UpdateManager("myElementId");
11906  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11907  * mgr.on("update", myFcnNeedsToKnow);
11908  * <br>
11909    // short handed call directly from the element object
11910    Roo.get("foo").load({
11911         url: "bar.php",
11912         scripts:true,
11913         params: "for=bar",
11914         text: "Loading Foo..."
11915    });
11916  * </code></pre>
11917  * @constructor
11918  * Create new UpdateManager directly.
11919  * @param {String/HTMLElement/Roo.Element} el The element to update
11920  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11921  */
11922 Roo.UpdateManager = function(el, forceNew){
11923     el = Roo.get(el);
11924     if(!forceNew && el.updateManager){
11925         return el.updateManager;
11926     }
11927     /**
11928      * The Element object
11929      * @type Roo.Element
11930      */
11931     this.el = el;
11932     /**
11933      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11934      * @type String
11935      */
11936     this.defaultUrl = null;
11937
11938     this.addEvents({
11939         /**
11940          * @event beforeupdate
11941          * Fired before an update is made, return false from your handler and the update is cancelled.
11942          * @param {Roo.Element} el
11943          * @param {String/Object/Function} url
11944          * @param {String/Object} params
11945          */
11946         "beforeupdate": true,
11947         /**
11948          * @event update
11949          * Fired after successful update is made.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "update": true,
11954         /**
11955          * @event failure
11956          * Fired on update failure.
11957          * @param {Roo.Element} el
11958          * @param {Object} oResponseObject The response Object
11959          */
11960         "failure": true
11961     });
11962     var d = Roo.UpdateManager.defaults;
11963     /**
11964      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11965      * @type String
11966      */
11967     this.sslBlankUrl = d.sslBlankUrl;
11968     /**
11969      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11970      * @type Boolean
11971      */
11972     this.disableCaching = d.disableCaching;
11973     /**
11974      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11975      * @type String
11976      */
11977     this.indicatorText = d.indicatorText;
11978     /**
11979      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11980      * @type String
11981      */
11982     this.showLoadIndicator = d.showLoadIndicator;
11983     /**
11984      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11985      * @type Number
11986      */
11987     this.timeout = d.timeout;
11988
11989     /**
11990      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11991      * @type Boolean
11992      */
11993     this.loadScripts = d.loadScripts;
11994
11995     /**
11996      * Transaction object of current executing transaction
11997      */
11998     this.transaction = null;
11999
12000     /**
12001      * @private
12002      */
12003     this.autoRefreshProcId = null;
12004     /**
12005      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.refreshDelegate = this.refresh.createDelegate(this);
12009     /**
12010      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.updateDelegate = this.update.createDelegate(this);
12014     /**
12015      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12016      * @type Function
12017      */
12018     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12019     /**
12020      * @private
12021      */
12022     this.successDelegate = this.processSuccess.createDelegate(this);
12023     /**
12024      * @private
12025      */
12026     this.failureDelegate = this.processFailure.createDelegate(this);
12027
12028     if(!this.renderer){
12029      /**
12030       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12031       */
12032     this.renderer = new Roo.UpdateManager.BasicRenderer();
12033     }
12034     
12035     Roo.UpdateManager.superclass.constructor.call(this);
12036 };
12037
12038 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12039     /**
12040      * Get the Element this UpdateManager is bound to
12041      * @return {Roo.Element} The element
12042      */
12043     getEl : function(){
12044         return this.el;
12045     },
12046     /**
12047      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12048      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12049 <pre><code>
12050 um.update({<br/>
12051     url: "your-url.php",<br/>
12052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12053     callback: yourFunction,<br/>
12054     scope: yourObject, //(optional scope)  <br/>
12055     discardUrl: false, <br/>
12056     nocache: false,<br/>
12057     text: "Loading...",<br/>
12058     timeout: 30,<br/>
12059     scripts: false<br/>
12060 });
12061 </code></pre>
12062      * The only required property is url. The optional properties nocache, text and scripts
12063      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12064      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12065      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12066      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12067      */
12068     update : function(url, params, callback, discardUrl){
12069         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12070             var method = this.method,
12071                 cfg;
12072             if(typeof url == "object"){ // must be config object
12073                 cfg = url;
12074                 url = cfg.url;
12075                 params = params || cfg.params;
12076                 callback = callback || cfg.callback;
12077                 discardUrl = discardUrl || cfg.discardUrl;
12078                 if(callback && cfg.scope){
12079                     callback = callback.createDelegate(cfg.scope);
12080                 }
12081                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12082                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12083                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12084                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12085                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12086             }
12087             this.showLoading();
12088             if(!discardUrl){
12089                 this.defaultUrl = url;
12090             }
12091             if(typeof url == "function"){
12092                 url = url.call(this);
12093             }
12094
12095             method = method || (params ? "POST" : "GET");
12096             if(method == "GET"){
12097                 url = this.prepareUrl(url);
12098             }
12099
12100             var o = Roo.apply(cfg ||{}, {
12101                 url : url,
12102                 params: params,
12103                 success: this.successDelegate,
12104                 failure: this.failureDelegate,
12105                 callback: undefined,
12106                 timeout: (this.timeout*1000),
12107                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12108             });
12109             Roo.log("updated manager called with timeout of " + o.timeout);
12110             this.transaction = Roo.Ajax.request(o);
12111         }
12112     },
12113
12114     /**
12115      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12116      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12117      * @param {String/HTMLElement} form The form Id or form element
12118      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12119      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12121      */
12122     formUpdate : function(form, url, reset, callback){
12123         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12124             if(typeof url == "function"){
12125                 url = url.call(this);
12126             }
12127             form = Roo.getDom(form);
12128             this.transaction = Roo.Ajax.request({
12129                 form: form,
12130                 url:url,
12131                 success: this.successDelegate,
12132                 failure: this.failureDelegate,
12133                 timeout: (this.timeout*1000),
12134                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12135             });
12136             this.showLoading.defer(1, this);
12137         }
12138     },
12139
12140     /**
12141      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12142      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12143      */
12144     refresh : function(callback){
12145         if(this.defaultUrl == null){
12146             return;
12147         }
12148         this.update(this.defaultUrl, null, callback, true);
12149     },
12150
12151     /**
12152      * Set this element to auto refresh.
12153      * @param {Number} interval How often to update (in seconds).
12154      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12155      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12157      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12158      */
12159     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12160         if(refreshNow){
12161             this.update(url || this.defaultUrl, params, callback, true);
12162         }
12163         if(this.autoRefreshProcId){
12164             clearInterval(this.autoRefreshProcId);
12165         }
12166         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12167     },
12168
12169     /**
12170      * Stop auto refresh on this element.
12171      */
12172      stopAutoRefresh : function(){
12173         if(this.autoRefreshProcId){
12174             clearInterval(this.autoRefreshProcId);
12175             delete this.autoRefreshProcId;
12176         }
12177     },
12178
12179     isAutoRefreshing : function(){
12180        return this.autoRefreshProcId ? true : false;
12181     },
12182     /**
12183      * Called to update the element to "Loading" state. Override to perform custom action.
12184      */
12185     showLoading : function(){
12186         if(this.showLoadIndicator){
12187             this.el.update(this.indicatorText);
12188         }
12189     },
12190
12191     /**
12192      * Adds unique parameter to query string if disableCaching = true
12193      * @private
12194      */
12195     prepareUrl : function(url){
12196         if(this.disableCaching){
12197             var append = "_dc=" + (new Date().getTime());
12198             if(url.indexOf("?") !== -1){
12199                 url += "&" + append;
12200             }else{
12201                 url += "?" + append;
12202             }
12203         }
12204         return url;
12205     },
12206
12207     /**
12208      * @private
12209      */
12210     processSuccess : function(response){
12211         this.transaction = null;
12212         if(response.argument.form && response.argument.reset){
12213             try{ // put in try/catch since some older FF releases had problems with this
12214                 response.argument.form.reset();
12215             }catch(e){}
12216         }
12217         if(this.loadScripts){
12218             this.renderer.render(this.el, response, this,
12219                 this.updateComplete.createDelegate(this, [response]));
12220         }else{
12221             this.renderer.render(this.el, response, this);
12222             this.updateComplete(response);
12223         }
12224     },
12225
12226     updateComplete : function(response){
12227         this.fireEvent("update", this.el, response);
12228         if(typeof response.argument.callback == "function"){
12229             response.argument.callback(this.el, true, response);
12230         }
12231     },
12232
12233     /**
12234      * @private
12235      */
12236     processFailure : function(response){
12237         this.transaction = null;
12238         this.fireEvent("failure", this.el, response);
12239         if(typeof response.argument.callback == "function"){
12240             response.argument.callback(this.el, false, response);
12241         }
12242     },
12243
12244     /**
12245      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12246      * @param {Object} renderer The object implementing the render() method
12247      */
12248     setRenderer : function(renderer){
12249         this.renderer = renderer;
12250     },
12251
12252     getRenderer : function(){
12253        return this.renderer;
12254     },
12255
12256     /**
12257      * Set the defaultUrl used for updates
12258      * @param {String/Function} defaultUrl The url or a function to call to get the url
12259      */
12260     setDefaultUrl : function(defaultUrl){
12261         this.defaultUrl = defaultUrl;
12262     },
12263
12264     /**
12265      * Aborts the executing transaction
12266      */
12267     abort : function(){
12268         if(this.transaction){
12269             Roo.Ajax.abort(this.transaction);
12270         }
12271     },
12272
12273     /**
12274      * Returns true if an update is in progress
12275      * @return {Boolean}
12276      */
12277     isUpdating : function(){
12278         if(this.transaction){
12279             return Roo.Ajax.isLoading(this.transaction);
12280         }
12281         return false;
12282     }
12283 });
12284
12285 /**
12286  * @class Roo.UpdateManager.defaults
12287  * @static (not really - but it helps the doc tool)
12288  * The defaults collection enables customizing the default properties of UpdateManager
12289  */
12290    Roo.UpdateManager.defaults = {
12291        /**
12292          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12293          * @type Number
12294          */
12295          timeout : 30,
12296
12297          /**
12298          * True to process scripts by default (Defaults to false).
12299          * @type Boolean
12300          */
12301         loadScripts : false,
12302
12303         /**
12304         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12305         * @type String
12306         */
12307         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12308         /**
12309          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12310          * @type Boolean
12311          */
12312         disableCaching : false,
12313         /**
12314          * Whether to show indicatorText when loading (Defaults to true).
12315          * @type Boolean
12316          */
12317         showLoadIndicator : true,
12318         /**
12319          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12320          * @type String
12321          */
12322         indicatorText : '<div class="loading-indicator">Loading...</div>'
12323    };
12324
12325 /**
12326  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12327  *Usage:
12328  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12329  * @param {String/HTMLElement/Roo.Element} el The element to update
12330  * @param {String} url The url
12331  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12332  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12333  * @static
12334  * @deprecated
12335  * @member Roo.UpdateManager
12336  */
12337 Roo.UpdateManager.updateElement = function(el, url, params, options){
12338     var um = Roo.get(el, true).getUpdateManager();
12339     Roo.apply(um, options);
12340     um.update(url, params, options ? options.callback : null);
12341 };
12342 // alias for backwards compat
12343 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12344 /**
12345  * @class Roo.UpdateManager.BasicRenderer
12346  * Default Content renderer. Updates the elements innerHTML with the responseText.
12347  */
12348 Roo.UpdateManager.BasicRenderer = function(){};
12349
12350 Roo.UpdateManager.BasicRenderer.prototype = {
12351     /**
12352      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12353      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12354      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12355      * @param {Roo.Element} el The element being rendered
12356      * @param {Object} response The YUI Connect response object
12357      * @param {UpdateManager} updateManager The calling update manager
12358      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12359      */
12360      render : function(el, response, updateManager, callback){
12361         el.update(response.responseText, updateManager.loadScripts, callback);
12362     }
12363 };
12364 /*
12365  * Based on:
12366  * Roo JS
12367  * (c)) Alan Knowles
12368  * Licence : LGPL
12369  */
12370
12371
12372 /**
12373  * @class Roo.DomTemplate
12374  * @extends Roo.Template
12375  * An effort at a dom based template engine..
12376  *
12377  * Similar to XTemplate, except it uses dom parsing to create the template..
12378  *
12379  * Supported features:
12380  *
12381  *  Tags:
12382
12383 <pre><code>
12384       {a_variable} - output encoded.
12385       {a_variable.format:("Y-m-d")} - call a method on the variable
12386       {a_variable:raw} - unencoded output
12387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12388       {a_variable:this.method_on_template(...)} - call a method on the template object.
12389  
12390 </code></pre>
12391  *  The tpl tag:
12392 <pre><code>
12393         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12394         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12395         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12396         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12397   
12398 </code></pre>
12399  *      
12400  */
12401 Roo.DomTemplate = function()
12402 {
12403      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12404      if (this.html) {
12405         this.compile();
12406      }
12407 };
12408
12409
12410 Roo.extend(Roo.DomTemplate, Roo.Template, {
12411     /**
12412      * id counter for sub templates.
12413      */
12414     id : 0,
12415     /**
12416      * flag to indicate if dom parser is inside a pre,
12417      * it will strip whitespace if not.
12418      */
12419     inPre : false,
12420     
12421     /**
12422      * The various sub templates
12423      */
12424     tpls : false,
12425     
12426     
12427     
12428     /**
12429      *
12430      * basic tag replacing syntax
12431      * WORD:WORD()
12432      *
12433      * // you can fake an object call by doing this
12434      *  x.t:(test,tesT) 
12435      * 
12436      */
12437     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12438     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12439     
12440     iterChild : function (node, method) {
12441         
12442         var oldPre = this.inPre;
12443         if (node.tagName == 'PRE') {
12444             this.inPre = true;
12445         }
12446         for( var i = 0; i < node.childNodes.length; i++) {
12447             method.call(this, node.childNodes[i]);
12448         }
12449         this.inPre = oldPre;
12450     },
12451     
12452     
12453     
12454     /**
12455      * compile the template
12456      *
12457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12458      *
12459      */
12460     compile: function()
12461     {
12462         var s = this.html;
12463         
12464         // covert the html into DOM...
12465         var doc = false;
12466         var div =false;
12467         try {
12468             doc = document.implementation.createHTMLDocument("");
12469             doc.documentElement.innerHTML =   this.html  ;
12470             div = doc.documentElement;
12471         } catch (e) {
12472             // old IE... - nasty -- it causes all sorts of issues.. with
12473             // images getting pulled from server..
12474             div = document.createElement('div');
12475             div.innerHTML = this.html;
12476         }
12477         //doc.documentElement.innerHTML = htmlBody
12478          
12479         
12480         
12481         this.tpls = [];
12482         var _t = this;
12483         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12484         
12485         var tpls = this.tpls;
12486         
12487         // create a top level template from the snippet..
12488         
12489         //Roo.log(div.innerHTML);
12490         
12491         var tpl = {
12492             uid : 'master',
12493             id : this.id++,
12494             attr : false,
12495             value : false,
12496             body : div.innerHTML,
12497             
12498             forCall : false,
12499             execCall : false,
12500             dom : div,
12501             isTop : true
12502             
12503         };
12504         tpls.unshift(tpl);
12505         
12506         
12507         // compile them...
12508         this.tpls = [];
12509         Roo.each(tpls, function(tp){
12510             this.compileTpl(tp);
12511             this.tpls[tp.id] = tp;
12512         }, this);
12513         
12514         this.master = tpls[0];
12515         return this;
12516         
12517         
12518     },
12519     
12520     compileNode : function(node, istop) {
12521         // test for
12522         //Roo.log(node);
12523         
12524         
12525         // skip anything not a tag..
12526         if (node.nodeType != 1) {
12527             if (node.nodeType == 3 && !this.inPre) {
12528                 // reduce white space..
12529                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12530                 
12531             }
12532             return;
12533         }
12534         
12535         var tpl = {
12536             uid : false,
12537             id : false,
12538             attr : false,
12539             value : false,
12540             body : '',
12541             
12542             forCall : false,
12543             execCall : false,
12544             dom : false,
12545             isTop : istop
12546             
12547             
12548         };
12549         
12550         
12551         switch(true) {
12552             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12553             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12554             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12555             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12556             // no default..
12557         }
12558         
12559         
12560         if (!tpl.attr) {
12561             // just itterate children..
12562             this.iterChild(node,this.compileNode);
12563             return;
12564         }
12565         tpl.uid = this.id++;
12566         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12567         node.removeAttribute('roo-'+ tpl.attr);
12568         if (tpl.attr != 'name') {
12569             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12570             node.parentNode.replaceChild(placeholder,  node);
12571         } else {
12572             
12573             var placeholder =  document.createElement('span');
12574             placeholder.className = 'roo-tpl-' + tpl.value;
12575             node.parentNode.replaceChild(placeholder,  node);
12576         }
12577         
12578         // parent now sees '{domtplXXXX}
12579         this.iterChild(node,this.compileNode);
12580         
12581         // we should now have node body...
12582         var div = document.createElement('div');
12583         div.appendChild(node);
12584         tpl.dom = node;
12585         // this has the unfortunate side effect of converting tagged attributes
12586         // eg. href="{...}" into %7C...%7D
12587         // this has been fixed by searching for those combo's although it's a bit hacky..
12588         
12589         
12590         tpl.body = div.innerHTML;
12591         
12592         
12593          
12594         tpl.id = tpl.uid;
12595         switch(tpl.attr) {
12596             case 'for' :
12597                 switch (tpl.value) {
12598                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12599                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12600                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12601                 }
12602                 break;
12603             
12604             case 'exec':
12605                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12606                 break;
12607             
12608             case 'if':     
12609                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12610                 break;
12611             
12612             case 'name':
12613                 tpl.id  = tpl.value; // replace non characters???
12614                 break;
12615             
12616         }
12617         
12618         
12619         this.tpls.push(tpl);
12620         
12621         
12622         
12623     },
12624     
12625     
12626     
12627     
12628     /**
12629      * Compile a segment of the template into a 'sub-template'
12630      *
12631      * 
12632      * 
12633      *
12634      */
12635     compileTpl : function(tpl)
12636     {
12637         var fm = Roo.util.Format;
12638         var useF = this.disableFormats !== true;
12639         
12640         var sep = Roo.isGecko ? "+\n" : ",\n";
12641         
12642         var undef = function(str) {
12643             Roo.debug && Roo.log("Property not found :"  + str);
12644             return '';
12645         };
12646           
12647         //Roo.log(tpl.body);
12648         
12649         
12650         
12651         var fn = function(m, lbrace, name, format, args)
12652         {
12653             //Roo.log("ARGS");
12654             //Roo.log(arguments);
12655             args = args ? args.replace(/\\'/g,"'") : args;
12656             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12657             if (typeof(format) == 'undefined') {
12658                 format =  'htmlEncode'; 
12659             }
12660             if (format == 'raw' ) {
12661                 format = false;
12662             }
12663             
12664             if(name.substr(0, 6) == 'domtpl'){
12665                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12666             }
12667             
12668             // build an array of options to determine if value is undefined..
12669             
12670             // basically get 'xxxx.yyyy' then do
12671             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12672             //    (function () { Roo.log("Property not found"); return ''; })() :
12673             //    ......
12674             
12675             var udef_ar = [];
12676             var lookfor = '';
12677             Roo.each(name.split('.'), function(st) {
12678                 lookfor += (lookfor.length ? '.': '') + st;
12679                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12680             });
12681             
12682             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12683             
12684             
12685             if(format && useF){
12686                 
12687                 args = args ? ',' + args : "";
12688                  
12689                 if(format.substr(0, 5) != "this."){
12690                     format = "fm." + format + '(';
12691                 }else{
12692                     format = 'this.call("'+ format.substr(5) + '", ';
12693                     args = ", values";
12694                 }
12695                 
12696                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12697             }
12698              
12699             if (args && args.length) {
12700                 // called with xxyx.yuu:(test,test)
12701                 // change to ()
12702                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12703             }
12704             // raw.. - :raw modifier..
12705             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12706             
12707         };
12708         var body;
12709         // branched to use + in gecko and [].join() in others
12710         if(Roo.isGecko){
12711             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12712                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12713                     "';};};";
12714         }else{
12715             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12716             body.push(tpl.body.replace(/(\r\n|\n)/g,
12717                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12718             body.push("'].join('');};};");
12719             body = body.join('');
12720         }
12721         
12722         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12723        
12724         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12725         eval(body);
12726         
12727         return this;
12728     },
12729      
12730     /**
12731      * same as applyTemplate, except it's done to one of the subTemplates
12732      * when using named templates, you can do:
12733      *
12734      * var str = pl.applySubTemplate('your-name', values);
12735      *
12736      * 
12737      * @param {Number} id of the template
12738      * @param {Object} values to apply to template
12739      * @param {Object} parent (normaly the instance of this object)
12740      */
12741     applySubTemplate : function(id, values, parent)
12742     {
12743         
12744         
12745         var t = this.tpls[id];
12746         
12747         
12748         try { 
12749             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12750                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12751                 return '';
12752             }
12753         } catch(e) {
12754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12755             Roo.log(values);
12756           
12757             return '';
12758         }
12759         try { 
12760             
12761             if(t.execCall && t.execCall.call(this, values, parent)){
12762                 return '';
12763             }
12764         } catch(e) {
12765             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12766             Roo.log(values);
12767             return '';
12768         }
12769         
12770         try {
12771             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12772             parent = t.target ? values : parent;
12773             if(t.forCall && vs instanceof Array){
12774                 var buf = [];
12775                 for(var i = 0, len = vs.length; i < len; i++){
12776                     try {
12777                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12778                     } catch (e) {
12779                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12780                         Roo.log(e.body);
12781                         //Roo.log(t.compiled);
12782                         Roo.log(vs[i]);
12783                     }   
12784                 }
12785                 return buf.join('');
12786             }
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12789             Roo.log(values);
12790             return '';
12791         }
12792         try {
12793             return t.compiled.call(this, vs, parent);
12794         } catch (e) {
12795             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12796             Roo.log(e.body);
12797             //Roo.log(t.compiled);
12798             Roo.log(values);
12799             return '';
12800         }
12801     },
12802
12803    
12804
12805     applyTemplate : function(values){
12806         return this.master.compiled.call(this, values, {});
12807         //var s = this.subs;
12808     },
12809
12810     apply : function(){
12811         return this.applyTemplate.apply(this, arguments);
12812     }
12813
12814  });
12815
12816 Roo.DomTemplate.from = function(el){
12817     el = Roo.getDom(el);
12818     return new Roo.Domtemplate(el.value || el.innerHTML);
12819 };/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830 /**
12831  * @class Roo.util.DelayedTask
12832  * Provides a convenient method of performing setTimeout where a new
12833  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12834  * You can use this class to buffer
12835  * the keypress events for a certain number of milliseconds, and perform only if they stop
12836  * for that amount of time.
12837  * @constructor The parameters to this constructor serve as defaults and are not required.
12838  * @param {Function} fn (optional) The default function to timeout
12839  * @param {Object} scope (optional) The default scope of that timeout
12840  * @param {Array} args (optional) The default Array of arguments
12841  */
12842 Roo.util.DelayedTask = function(fn, scope, args){
12843     var id = null, d, t;
12844
12845     var call = function(){
12846         var now = new Date().getTime();
12847         if(now - t >= d){
12848             clearInterval(id);
12849             id = null;
12850             fn.apply(scope, args || []);
12851         }
12852     };
12853     /**
12854      * Cancels any pending timeout and queues a new one
12855      * @param {Number} delay The milliseconds to delay
12856      * @param {Function} newFn (optional) Overrides function passed to constructor
12857      * @param {Object} newScope (optional) Overrides scope passed to constructor
12858      * @param {Array} newArgs (optional) Overrides args passed to constructor
12859      */
12860     this.delay = function(delay, newFn, newScope, newArgs){
12861         if(id && delay != d){
12862             this.cancel();
12863         }
12864         d = delay;
12865         t = new Date().getTime();
12866         fn = newFn || fn;
12867         scope = newScope || scope;
12868         args = newArgs || args;
12869         if(!id){
12870             id = setInterval(call, d);
12871         }
12872     };
12873
12874     /**
12875      * Cancel the last queued timeout
12876      */
12877     this.cancel = function(){
12878         if(id){
12879             clearInterval(id);
12880             id = null;
12881         }
12882     };
12883 };/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894  
12895 Roo.util.TaskRunner = function(interval){
12896     interval = interval || 10;
12897     var tasks = [], removeQueue = [];
12898     var id = 0;
12899     var running = false;
12900
12901     var stopThread = function(){
12902         running = false;
12903         clearInterval(id);
12904         id = 0;
12905     };
12906
12907     var startThread = function(){
12908         if(!running){
12909             running = true;
12910             id = setInterval(runTasks, interval);
12911         }
12912     };
12913
12914     var removeTask = function(task){
12915         removeQueue.push(task);
12916         if(task.onStop){
12917             task.onStop();
12918         }
12919     };
12920
12921     var runTasks = function(){
12922         if(removeQueue.length > 0){
12923             for(var i = 0, len = removeQueue.length; i < len; i++){
12924                 tasks.remove(removeQueue[i]);
12925             }
12926             removeQueue = [];
12927             if(tasks.length < 1){
12928                 stopThread();
12929                 return;
12930             }
12931         }
12932         var now = new Date().getTime();
12933         for(var i = 0, len = tasks.length; i < len; ++i){
12934             var t = tasks[i];
12935             var itime = now - t.taskRunTime;
12936             if(t.interval <= itime){
12937                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12938                 t.taskRunTime = now;
12939                 if(rt === false || t.taskRunCount === t.repeat){
12940                     removeTask(t);
12941                     return;
12942                 }
12943             }
12944             if(t.duration && t.duration <= (now - t.taskStartTime)){
12945                 removeTask(t);
12946             }
12947         }
12948     };
12949
12950     /**
12951      * Queues a new task.
12952      * @param {Object} task
12953      */
12954     this.start = function(task){
12955         tasks.push(task);
12956         task.taskStartTime = new Date().getTime();
12957         task.taskRunTime = 0;
12958         task.taskRunCount = 0;
12959         startThread();
12960         return task;
12961     };
12962
12963     this.stop = function(task){
12964         removeTask(task);
12965         return task;
12966     };
12967
12968     this.stopAll = function(){
12969         stopThread();
12970         for(var i = 0, len = tasks.length; i < len; i++){
12971             if(tasks[i].onStop){
12972                 tasks[i].onStop();
12973             }
12974         }
12975         tasks = [];
12976         removeQueue = [];
12977     };
12978 };
12979
12980 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991  
12992 /**
12993  * @class Roo.util.MixedCollection
12994  * @extends Roo.util.Observable
12995  * A Collection class that maintains both numeric indexes and keys and exposes events.
12996  * @constructor
12997  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12998  * collection (defaults to false)
12999  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13000  * and return the key value for that item.  This is used when available to look up the key on items that
13001  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13002  * equivalent to providing an implementation for the {@link #getKey} method.
13003  */
13004 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13005     this.items = [];
13006     this.map = {};
13007     this.keys = [];
13008     this.length = 0;
13009     this.addEvents({
13010         /**
13011          * @event clear
13012          * Fires when the collection is cleared.
13013          */
13014         "clear" : true,
13015         /**
13016          * @event add
13017          * Fires when an item is added to the collection.
13018          * @param {Number} index The index at which the item was added.
13019          * @param {Object} o The item added.
13020          * @param {String} key The key associated with the added item.
13021          */
13022         "add" : true,
13023         /**
13024          * @event replace
13025          * Fires when an item is replaced in the collection.
13026          * @param {String} key he key associated with the new added.
13027          * @param {Object} old The item being replaced.
13028          * @param {Object} new The new item.
13029          */
13030         "replace" : true,
13031         /**
13032          * @event remove
13033          * Fires when an item is removed from the collection.
13034          * @param {Object} o The item being removed.
13035          * @param {String} key (optional) The key associated with the removed item.
13036          */
13037         "remove" : true,
13038         "sort" : true
13039     });
13040     this.allowFunctions = allowFunctions === true;
13041     if(keyFn){
13042         this.getKey = keyFn;
13043     }
13044     Roo.util.MixedCollection.superclass.constructor.call(this);
13045 };
13046
13047 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13048     allowFunctions : false,
13049     
13050 /**
13051  * Adds an item to the collection.
13052  * @param {String} key The key to associate with the item
13053  * @param {Object} o The item to add.
13054  * @return {Object} The item added.
13055  */
13056     add : function(key, o){
13057         if(arguments.length == 1){
13058             o = arguments[0];
13059             key = this.getKey(o);
13060         }
13061         if(typeof key == "undefined" || key === null){
13062             this.length++;
13063             this.items.push(o);
13064             this.keys.push(null);
13065         }else{
13066             var old = this.map[key];
13067             if(old){
13068                 return this.replace(key, o);
13069             }
13070             this.length++;
13071             this.items.push(o);
13072             this.map[key] = o;
13073             this.keys.push(key);
13074         }
13075         this.fireEvent("add", this.length-1, o, key);
13076         return o;
13077     },
13078        
13079 /**
13080   * MixedCollection has a generic way to fetch keys if you implement getKey.
13081 <pre><code>
13082 // normal way
13083 var mc = new Roo.util.MixedCollection();
13084 mc.add(someEl.dom.id, someEl);
13085 mc.add(otherEl.dom.id, otherEl);
13086 //and so on
13087
13088 // using getKey
13089 var mc = new Roo.util.MixedCollection();
13090 mc.getKey = function(el){
13091    return el.dom.id;
13092 };
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095
13096 // or via the constructor
13097 var mc = new Roo.util.MixedCollection(false, function(el){
13098    return el.dom.id;
13099 });
13100 mc.add(someEl);
13101 mc.add(otherEl);
13102 </code></pre>
13103  * @param o {Object} The item for which to find the key.
13104  * @return {Object} The key for the passed item.
13105  */
13106     getKey : function(o){
13107          return o.id; 
13108     },
13109    
13110 /**
13111  * Replaces an item in the collection.
13112  * @param {String} key The key associated with the item to replace, or the item to replace.
13113  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13114  * @return {Object}  The new item.
13115  */
13116     replace : function(key, o){
13117         if(arguments.length == 1){
13118             o = arguments[0];
13119             key = this.getKey(o);
13120         }
13121         var old = this.item(key);
13122         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13123              return this.add(key, o);
13124         }
13125         var index = this.indexOfKey(key);
13126         this.items[index] = o;
13127         this.map[key] = o;
13128         this.fireEvent("replace", key, old, o);
13129         return o;
13130     },
13131    
13132 /**
13133  * Adds all elements of an Array or an Object to the collection.
13134  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13135  * an Array of values, each of which are added to the collection.
13136  */
13137     addAll : function(objs){
13138         if(arguments.length > 1 || objs instanceof Array){
13139             var args = arguments.length > 1 ? arguments : objs;
13140             for(var i = 0, len = args.length; i < len; i++){
13141                 this.add(args[i]);
13142             }
13143         }else{
13144             for(var key in objs){
13145                 if(this.allowFunctions || typeof objs[key] != "function"){
13146                     this.add(key, objs[key]);
13147                 }
13148             }
13149         }
13150     },
13151    
13152 /**
13153  * Executes the specified function once for every item in the collection, passing each
13154  * item as the first and only parameter. returning false from the function will stop the iteration.
13155  * @param {Function} fn The function to execute for each item.
13156  * @param {Object} scope (optional) The scope in which to execute the function.
13157  */
13158     each : function(fn, scope){
13159         var items = [].concat(this.items); // each safe for removal
13160         for(var i = 0, len = items.length; i < len; i++){
13161             if(fn.call(scope || items[i], items[i], i, len) === false){
13162                 break;
13163             }
13164         }
13165     },
13166    
13167 /**
13168  * Executes the specified function once for every key in the collection, passing each
13169  * key, and its associated item as the first two parameters.
13170  * @param {Function} fn The function to execute for each item.
13171  * @param {Object} scope (optional) The scope in which to execute the function.
13172  */
13173     eachKey : function(fn, scope){
13174         for(var i = 0, len = this.keys.length; i < len; i++){
13175             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the first item in the collection which elicits a true return value from the
13181  * passed selection function.
13182  * @param {Function} fn The selection function to execute for each item.
13183  * @param {Object} scope (optional) The scope in which to execute the function.
13184  * @return {Object} The first item in the collection which returned true from the selection function.
13185  */
13186     find : function(fn, scope){
13187         for(var i = 0, len = this.items.length; i < len; i++){
13188             if(fn.call(scope || window, this.items[i], this.keys[i])){
13189                 return this.items[i];
13190             }
13191         }
13192         return null;
13193     },
13194    
13195 /**
13196  * Inserts an item at the specified index in the collection.
13197  * @param {Number} index The index to insert the item at.
13198  * @param {String} key The key to associate with the new item, or the item itself.
13199  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13200  * @return {Object} The item inserted.
13201  */
13202     insert : function(index, key, o){
13203         if(arguments.length == 2){
13204             o = arguments[1];
13205             key = this.getKey(o);
13206         }
13207         if(index >= this.length){
13208             return this.add(key, o);
13209         }
13210         this.length++;
13211         this.items.splice(index, 0, o);
13212         if(typeof key != "undefined" && key != null){
13213             this.map[key] = o;
13214         }
13215         this.keys.splice(index, 0, key);
13216         this.fireEvent("add", index, o, key);
13217         return o;
13218     },
13219    
13220 /**
13221  * Removed an item from the collection.
13222  * @param {Object} o The item to remove.
13223  * @return {Object} The item removed.
13224  */
13225     remove : function(o){
13226         return this.removeAt(this.indexOf(o));
13227     },
13228    
13229 /**
13230  * Remove an item from a specified index in the collection.
13231  * @param {Number} index The index within the collection of the item to remove.
13232  */
13233     removeAt : function(index){
13234         if(index < this.length && index >= 0){
13235             this.length--;
13236             var o = this.items[index];
13237             this.items.splice(index, 1);
13238             var key = this.keys[index];
13239             if(typeof key != "undefined"){
13240                 delete this.map[key];
13241             }
13242             this.keys.splice(index, 1);
13243             this.fireEvent("remove", o, key);
13244         }
13245     },
13246    
13247 /**
13248  * Removed an item associated with the passed key fom the collection.
13249  * @param {String} key The key of the item to remove.
13250  */
13251     removeKey : function(key){
13252         return this.removeAt(this.indexOfKey(key));
13253     },
13254    
13255 /**
13256  * Returns the number of items in the collection.
13257  * @return {Number} the number of items in the collection.
13258  */
13259     getCount : function(){
13260         return this.length; 
13261     },
13262    
13263 /**
13264  * Returns index within the collection of the passed Object.
13265  * @param {Object} o The item to find the index of.
13266  * @return {Number} index of the item.
13267  */
13268     indexOf : function(o){
13269         if(!this.items.indexOf){
13270             for(var i = 0, len = this.items.length; i < len; i++){
13271                 if(this.items[i] == o) {
13272                     return i;
13273                 }
13274             }
13275             return -1;
13276         }else{
13277             return this.items.indexOf(o);
13278         }
13279     },
13280    
13281 /**
13282  * Returns index within the collection of the passed key.
13283  * @param {String} key The key to find the index of.
13284  * @return {Number} index of the key.
13285  */
13286     indexOfKey : function(key){
13287         if(!this.keys.indexOf){
13288             for(var i = 0, len = this.keys.length; i < len; i++){
13289                 if(this.keys[i] == key) {
13290                     return i;
13291                 }
13292             }
13293             return -1;
13294         }else{
13295             return this.keys.indexOf(key);
13296         }
13297     },
13298    
13299 /**
13300  * Returns the item associated with the passed key OR index. Key has priority over index.
13301  * @param {String/Number} key The key or index of the item.
13302  * @return {Object} The item associated with the passed key.
13303  */
13304     item : function(key){
13305         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13306         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13307     },
13308     
13309 /**
13310  * Returns the item at the specified index.
13311  * @param {Number} index The index of the item.
13312  * @return {Object}
13313  */
13314     itemAt : function(index){
13315         return this.items[index];
13316     },
13317     
13318 /**
13319  * Returns the item associated with the passed key.
13320  * @param {String/Number} key The key of the item.
13321  * @return {Object} The item associated with the passed key.
13322  */
13323     key : function(key){
13324         return this.map[key];
13325     },
13326    
13327 /**
13328  * Returns true if the collection contains the passed Object as an item.
13329  * @param {Object} o  The Object to look for in the collection.
13330  * @return {Boolean} True if the collection contains the Object as an item.
13331  */
13332     contains : function(o){
13333         return this.indexOf(o) != -1;
13334     },
13335    
13336 /**
13337  * Returns true if the collection contains the passed Object as a key.
13338  * @param {String} key The key to look for in the collection.
13339  * @return {Boolean} True if the collection contains the Object as a key.
13340  */
13341     containsKey : function(key){
13342         return typeof this.map[key] != "undefined";
13343     },
13344    
13345 /**
13346  * Removes all items from the collection.
13347  */
13348     clear : function(){
13349         this.length = 0;
13350         this.items = [];
13351         this.keys = [];
13352         this.map = {};
13353         this.fireEvent("clear");
13354     },
13355    
13356 /**
13357  * Returns the first item in the collection.
13358  * @return {Object} the first item in the collection..
13359  */
13360     first : function(){
13361         return this.items[0]; 
13362     },
13363    
13364 /**
13365  * Returns the last item in the collection.
13366  * @return {Object} the last item in the collection..
13367  */
13368     last : function(){
13369         return this.items[this.length-1];   
13370     },
13371     
13372     _sort : function(property, dir, fn){
13373         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13374         fn = fn || function(a, b){
13375             return a-b;
13376         };
13377         var c = [], k = this.keys, items = this.items;
13378         for(var i = 0, len = items.length; i < len; i++){
13379             c[c.length] = {key: k[i], value: items[i], index: i};
13380         }
13381         c.sort(function(a, b){
13382             var v = fn(a[property], b[property]) * dsc;
13383             if(v == 0){
13384                 v = (a.index < b.index ? -1 : 1);
13385             }
13386             return v;
13387         });
13388         for(var i = 0, len = c.length; i < len; i++){
13389             items[i] = c[i].value;
13390             k[i] = c[i].key;
13391         }
13392         this.fireEvent("sort", this);
13393     },
13394     
13395     /**
13396      * Sorts this collection with the passed comparison function
13397      * @param {String} direction (optional) "ASC" or "DESC"
13398      * @param {Function} fn (optional) comparison function
13399      */
13400     sort : function(dir, fn){
13401         this._sort("value", dir, fn);
13402     },
13403     
13404     /**
13405      * Sorts this collection by keys
13406      * @param {String} direction (optional) "ASC" or "DESC"
13407      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13408      */
13409     keySort : function(dir, fn){
13410         this._sort("key", dir, fn || function(a, b){
13411             return String(a).toUpperCase()-String(b).toUpperCase();
13412         });
13413     },
13414     
13415     /**
13416      * Returns a range of items in this collection
13417      * @param {Number} startIndex (optional) defaults to 0
13418      * @param {Number} endIndex (optional) default to the last item
13419      * @return {Array} An array of items
13420      */
13421     getRange : function(start, end){
13422         var items = this.items;
13423         if(items.length < 1){
13424             return [];
13425         }
13426         start = start || 0;
13427         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13428         var r = [];
13429         if(start <= end){
13430             for(var i = start; i <= end; i++) {
13431                     r[r.length] = items[i];
13432             }
13433         }else{
13434             for(var i = start; i >= end; i--) {
13435                     r[r.length] = items[i];
13436             }
13437         }
13438         return r;
13439     },
13440         
13441     /**
13442      * Filter the <i>objects</i> in this collection by a specific property. 
13443      * Returns a new collection that has been filtered.
13444      * @param {String} property A property on your objects
13445      * @param {String/RegExp} value Either string that the property values 
13446      * should start with or a RegExp to test against the property
13447      * @return {MixedCollection} The new filtered collection
13448      */
13449     filter : function(property, value){
13450         if(!value.exec){ // not a regex
13451             value = String(value);
13452             if(value.length == 0){
13453                 return this.clone();
13454             }
13455             value = new RegExp("^" + Roo.escapeRe(value), "i");
13456         }
13457         return this.filterBy(function(o){
13458             return o && value.test(o[property]);
13459         });
13460         },
13461     
13462     /**
13463      * Filter by a function. * Returns a new collection that has been filtered.
13464      * The passed function will be called with each 
13465      * object in the collection. If the function returns true, the value is included 
13466      * otherwise it is filtered.
13467      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13468      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13469      * @return {MixedCollection} The new filtered collection
13470      */
13471     filterBy : function(fn, scope){
13472         var r = new Roo.util.MixedCollection();
13473         r.getKey = this.getKey;
13474         var k = this.keys, it = this.items;
13475         for(var i = 0, len = it.length; i < len; i++){
13476             if(fn.call(scope||this, it[i], k[i])){
13477                                 r.add(k[i], it[i]);
13478                         }
13479         }
13480         return r;
13481     },
13482     
13483     /**
13484      * Creates a duplicate of this collection
13485      * @return {MixedCollection}
13486      */
13487     clone : function(){
13488         var r = new Roo.util.MixedCollection();
13489         var k = this.keys, it = this.items;
13490         for(var i = 0, len = it.length; i < len; i++){
13491             r.add(k[i], it[i]);
13492         }
13493         r.getKey = this.getKey;
13494         return r;
13495     }
13496 });
13497 /**
13498  * Returns the item associated with the passed key or index.
13499  * @method
13500  * @param {String/Number} key The key or index of the item.
13501  * @return {Object} The item associated with the passed key.
13502  */
13503 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13504  * Based on:
13505  * Ext JS Library 1.1.1
13506  * Copyright(c) 2006-2007, Ext JS, LLC.
13507  *
13508  * Originally Released Under LGPL - original licence link has changed is not relivant.
13509  *
13510  * Fork - LGPL
13511  * <script type="text/javascript">
13512  */
13513 /**
13514  * @class Roo.util.JSON
13515  * Modified version of Douglas Crockford"s json.js that doesn"t
13516  * mess with the Object prototype 
13517  * http://www.json.org/js.html
13518  * @singleton
13519  */
13520 Roo.util.JSON = new (function(){
13521     var useHasOwn = {}.hasOwnProperty ? true : false;
13522     
13523     // crashes Safari in some instances
13524     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13525     
13526     var pad = function(n) {
13527         return n < 10 ? "0" + n : n;
13528     };
13529     
13530     var m = {
13531         "\b": '\\b',
13532         "\t": '\\t',
13533         "\n": '\\n',
13534         "\f": '\\f',
13535         "\r": '\\r',
13536         '"' : '\\"',
13537         "\\": '\\\\'
13538     };
13539
13540     var encodeString = function(s){
13541         if (/["\\\x00-\x1f]/.test(s)) {
13542             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13543                 var c = m[b];
13544                 if(c){
13545                     return c;
13546                 }
13547                 c = b.charCodeAt();
13548                 return "\\u00" +
13549                     Math.floor(c / 16).toString(16) +
13550                     (c % 16).toString(16);
13551             }) + '"';
13552         }
13553         return '"' + s + '"';
13554     };
13555     
13556     var encodeArray = function(o){
13557         var a = ["["], b, i, l = o.length, v;
13558             for (i = 0; i < l; i += 1) {
13559                 v = o[i];
13560                 switch (typeof v) {
13561                     case "undefined":
13562                     case "function":
13563                     case "unknown":
13564                         break;
13565                     default:
13566                         if (b) {
13567                             a.push(',');
13568                         }
13569                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13570                         b = true;
13571                 }
13572             }
13573             a.push("]");
13574             return a.join("");
13575     };
13576     
13577     var encodeDate = function(o){
13578         return '"' + o.getFullYear() + "-" +
13579                 pad(o.getMonth() + 1) + "-" +
13580                 pad(o.getDate()) + "T" +
13581                 pad(o.getHours()) + ":" +
13582                 pad(o.getMinutes()) + ":" +
13583                 pad(o.getSeconds()) + '"';
13584     };
13585     
13586     /**
13587      * Encodes an Object, Array or other value
13588      * @param {Mixed} o The variable to encode
13589      * @return {String} The JSON string
13590      */
13591     this.encode = function(o)
13592     {
13593         // should this be extended to fully wrap stringify..
13594         
13595         if(typeof o == "undefined" || o === null){
13596             return "null";
13597         }else if(o instanceof Array){
13598             return encodeArray(o);
13599         }else if(o instanceof Date){
13600             return encodeDate(o);
13601         }else if(typeof o == "string"){
13602             return encodeString(o);
13603         }else if(typeof o == "number"){
13604             return isFinite(o) ? String(o) : "null";
13605         }else if(typeof o == "boolean"){
13606             return String(o);
13607         }else {
13608             var a = ["{"], b, i, v;
13609             for (i in o) {
13610                 if(!useHasOwn || o.hasOwnProperty(i)) {
13611                     v = o[i];
13612                     switch (typeof v) {
13613                     case "undefined":
13614                     case "function":
13615                     case "unknown":
13616                         break;
13617                     default:
13618                         if(b){
13619                             a.push(',');
13620                         }
13621                         a.push(this.encode(i), ":",
13622                                 v === null ? "null" : this.encode(v));
13623                         b = true;
13624                     }
13625                 }
13626             }
13627             a.push("}");
13628             return a.join("");
13629         }
13630     };
13631     
13632     /**
13633      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13634      * @param {String} json The JSON string
13635      * @return {Object} The resulting object
13636      */
13637     this.decode = function(json){
13638         
13639         return  /** eval:var:json */ eval("(" + json + ')');
13640     };
13641 })();
13642 /** 
13643  * Shorthand for {@link Roo.util.JSON#encode}
13644  * @member Roo encode 
13645  * @method */
13646 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13647 /** 
13648  * Shorthand for {@link Roo.util.JSON#decode}
13649  * @member Roo decode 
13650  * @method */
13651 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13652 /*
13653  * Based on:
13654  * Ext JS Library 1.1.1
13655  * Copyright(c) 2006-2007, Ext JS, LLC.
13656  *
13657  * Originally Released Under LGPL - original licence link has changed is not relivant.
13658  *
13659  * Fork - LGPL
13660  * <script type="text/javascript">
13661  */
13662  
13663 /**
13664  * @class Roo.util.Format
13665  * Reusable data formatting functions
13666  * @singleton
13667  */
13668 Roo.util.Format = function(){
13669     var trimRe = /^\s+|\s+$/g;
13670     return {
13671         /**
13672          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13673          * @param {String} value The string to truncate
13674          * @param {Number} length The maximum length to allow before truncating
13675          * @return {String} The converted text
13676          */
13677         ellipsis : function(value, len){
13678             if(value && value.length > len){
13679                 return value.substr(0, len-3)+"...";
13680             }
13681             return value;
13682         },
13683
13684         /**
13685          * Checks a reference and converts it to empty string if it is undefined
13686          * @param {Mixed} value Reference to check
13687          * @return {Mixed} Empty string if converted, otherwise the original value
13688          */
13689         undef : function(value){
13690             return typeof value != "undefined" ? value : "";
13691         },
13692
13693         /**
13694          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13695          * @param {String} value The string to encode
13696          * @return {String} The encoded text
13697          */
13698         htmlEncode : function(value){
13699             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13700         },
13701
13702         /**
13703          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13704          * @param {String} value The string to decode
13705          * @return {String} The decoded text
13706          */
13707         htmlDecode : function(value){
13708             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13709         },
13710
13711         /**
13712          * Trims any whitespace from either side of a string
13713          * @param {String} value The text to trim
13714          * @return {String} The trimmed text
13715          */
13716         trim : function(value){
13717             return String(value).replace(trimRe, "");
13718         },
13719
13720         /**
13721          * Returns a substring from within an original string
13722          * @param {String} value The original text
13723          * @param {Number} start The start index of the substring
13724          * @param {Number} length The length of the substring
13725          * @return {String} The substring
13726          */
13727         substr : function(value, start, length){
13728             return String(value).substr(start, length);
13729         },
13730
13731         /**
13732          * Converts a string to all lower case letters
13733          * @param {String} value The text to convert
13734          * @return {String} The converted text
13735          */
13736         lowercase : function(value){
13737             return String(value).toLowerCase();
13738         },
13739
13740         /**
13741          * Converts a string to all upper case letters
13742          * @param {String} value The text to convert
13743          * @return {String} The converted text
13744          */
13745         uppercase : function(value){
13746             return String(value).toUpperCase();
13747         },
13748
13749         /**
13750          * Converts the first character only of a string to upper case
13751          * @param {String} value The text to convert
13752          * @return {String} The converted text
13753          */
13754         capitalize : function(value){
13755             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13756         },
13757
13758         // private
13759         call : function(value, fn){
13760             if(arguments.length > 2){
13761                 var args = Array.prototype.slice.call(arguments, 2);
13762                 args.unshift(value);
13763                  
13764                 return /** eval:var:value */  eval(fn).apply(window, args);
13765             }else{
13766                 /** eval:var:value */
13767                 return /** eval:var:value */ eval(fn).call(window, value);
13768             }
13769         },
13770
13771        
13772         /**
13773          * safer version of Math.toFixed..??/
13774          * @param {Number/String} value The numeric value to format
13775          * @param {Number/String} value Decimal places 
13776          * @return {String} The formatted currency string
13777          */
13778         toFixed : function(v, n)
13779         {
13780             // why not use to fixed - precision is buggered???
13781             if (!n) {
13782                 return Math.round(v-0);
13783             }
13784             var fact = Math.pow(10,n+1);
13785             v = (Math.round((v-0)*fact))/fact;
13786             var z = (''+fact).substring(2);
13787             if (v == Math.floor(v)) {
13788                 return Math.floor(v) + '.' + z;
13789             }
13790             
13791             // now just padd decimals..
13792             var ps = String(v).split('.');
13793             var fd = (ps[1] + z);
13794             var r = fd.substring(0,n); 
13795             var rm = fd.substring(n); 
13796             if (rm < 5) {
13797                 return ps[0] + '.' + r;
13798             }
13799             r*=1; // turn it into a number;
13800             r++;
13801             if (String(r).length != n) {
13802                 ps[0]*=1;
13803                 ps[0]++;
13804                 r = String(r).substring(1); // chop the end off.
13805             }
13806             
13807             return ps[0] + '.' + r;
13808              
13809         },
13810         
13811         /**
13812          * Format a number as US currency
13813          * @param {Number/String} value The numeric value to format
13814          * @return {String} The formatted currency string
13815          */
13816         usMoney : function(v){
13817             return '$' + Roo.util.Format.number(v);
13818         },
13819         
13820         /**
13821          * Format a number
13822          * eventually this should probably emulate php's number_format
13823          * @param {Number/String} value The numeric value to format
13824          * @param {Number} decimals number of decimal places
13825          * @param {String} delimiter for thousands (default comma)
13826          * @return {String} The formatted currency string
13827          */
13828         number : function(v, decimals, thousandsDelimiter)
13829         {
13830             // multiply and round.
13831             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13832             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13833             
13834             var mul = Math.pow(10, decimals);
13835             var zero = String(mul).substring(1);
13836             v = (Math.round((v-0)*mul))/mul;
13837             
13838             // if it's '0' number.. then
13839             
13840             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13841             v = String(v);
13842             var ps = v.split('.');
13843             var whole = ps[0];
13844             
13845             var r = /(\d+)(\d{3})/;
13846             // add comma's
13847             
13848             if(thousandsDelimiter.length != 0) {
13849                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13850             } 
13851             
13852             var sub = ps[1] ?
13853                     // has decimals..
13854                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13855                     // does not have decimals
13856                     (decimals ? ('.' + zero) : '');
13857             
13858             
13859             return whole + sub ;
13860         },
13861         
13862         /**
13863          * Parse a value into a formatted date using the specified format pattern.
13864          * @param {Mixed} value The value to format
13865          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13866          * @return {String} The formatted date string
13867          */
13868         date : function(v, format){
13869             if(!v){
13870                 return "";
13871             }
13872             if(!(v instanceof Date)){
13873                 v = new Date(Date.parse(v));
13874             }
13875             return v.dateFormat(format || Roo.util.Format.defaults.date);
13876         },
13877
13878         /**
13879          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13880          * @param {String} format Any valid date format string
13881          * @return {Function} The date formatting function
13882          */
13883         dateRenderer : function(format){
13884             return function(v){
13885                 return Roo.util.Format.date(v, format);  
13886             };
13887         },
13888
13889         // private
13890         stripTagsRE : /<\/?[^>]+>/gi,
13891         
13892         /**
13893          * Strips all HTML tags
13894          * @param {Mixed} value The text from which to strip tags
13895          * @return {String} The stripped text
13896          */
13897         stripTags : function(v){
13898             return !v ? v : String(v).replace(this.stripTagsRE, "");
13899         }
13900     };
13901 }();
13902 Roo.util.Format.defaults = {
13903     date : 'd/M/Y'
13904 };/*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914
13915
13916  
13917
13918 /**
13919  * @class Roo.MasterTemplate
13920  * @extends Roo.Template
13921  * Provides a template that can have child templates. The syntax is:
13922 <pre><code>
13923 var t = new Roo.MasterTemplate(
13924         '&lt;select name="{name}"&gt;',
13925                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13926         '&lt;/select&gt;'
13927 );
13928 t.add('options', {value: 'foo', text: 'bar'});
13929 // or you can add multiple child elements in one shot
13930 t.addAll('options', [
13931     {value: 'foo', text: 'bar'},
13932     {value: 'foo2', text: 'bar2'},
13933     {value: 'foo3', text: 'bar3'}
13934 ]);
13935 // then append, applying the master template values
13936 t.append('my-form', {name: 'my-select'});
13937 </code></pre>
13938 * A name attribute for the child template is not required if you have only one child
13939 * template or you want to refer to them by index.
13940  */
13941 Roo.MasterTemplate = function(){
13942     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13943     this.originalHtml = this.html;
13944     var st = {};
13945     var m, re = this.subTemplateRe;
13946     re.lastIndex = 0;
13947     var subIndex = 0;
13948     while(m = re.exec(this.html)){
13949         var name = m[1], content = m[2];
13950         st[subIndex] = {
13951             name: name,
13952             index: subIndex,
13953             buffer: [],
13954             tpl : new Roo.Template(content)
13955         };
13956         if(name){
13957             st[name] = st[subIndex];
13958         }
13959         st[subIndex].tpl.compile();
13960         st[subIndex].tpl.call = this.call.createDelegate(this);
13961         subIndex++;
13962     }
13963     this.subCount = subIndex;
13964     this.subs = st;
13965 };
13966 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13967     /**
13968     * The regular expression used to match sub templates
13969     * @type RegExp
13970     * @property
13971     */
13972     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13973
13974     /**
13975      * Applies the passed values to a child template.
13976      * @param {String/Number} name (optional) The name or index of the child template
13977      * @param {Array/Object} values The values to be applied to the template
13978      * @return {MasterTemplate} this
13979      */
13980      add : function(name, values){
13981         if(arguments.length == 1){
13982             values = arguments[0];
13983             name = 0;
13984         }
13985         var s = this.subs[name];
13986         s.buffer[s.buffer.length] = s.tpl.apply(values);
13987         return this;
13988     },
13989
13990     /**
13991      * Applies all the passed values to a child template.
13992      * @param {String/Number} name (optional) The name or index of the child template
13993      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13994      * @param {Boolean} reset (optional) True to reset the template first
13995      * @return {MasterTemplate} this
13996      */
13997     fill : function(name, values, reset){
13998         var a = arguments;
13999         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14000             values = a[0];
14001             name = 0;
14002             reset = a[1];
14003         }
14004         if(reset){
14005             this.reset();
14006         }
14007         for(var i = 0, len = values.length; i < len; i++){
14008             this.add(name, values[i]);
14009         }
14010         return this;
14011     },
14012
14013     /**
14014      * Resets the template for reuse
14015      * @return {MasterTemplate} this
14016      */
14017      reset : function(){
14018         var s = this.subs;
14019         for(var i = 0; i < this.subCount; i++){
14020             s[i].buffer = [];
14021         }
14022         return this;
14023     },
14024
14025     applyTemplate : function(values){
14026         var s = this.subs;
14027         var replaceIndex = -1;
14028         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14029             return s[++replaceIndex].buffer.join("");
14030         });
14031         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14032     },
14033
14034     apply : function(){
14035         return this.applyTemplate.apply(this, arguments);
14036     },
14037
14038     compile : function(){return this;}
14039 });
14040
14041 /**
14042  * Alias for fill().
14043  * @method
14044  */
14045 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14046  /**
14047  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14048  * var tpl = Roo.MasterTemplate.from('element-id');
14049  * @param {String/HTMLElement} el
14050  * @param {Object} config
14051  * @static
14052  */
14053 Roo.MasterTemplate.from = function(el, config){
14054     el = Roo.getDom(el);
14055     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14056 };/*
14057  * Based on:
14058  * Ext JS Library 1.1.1
14059  * Copyright(c) 2006-2007, Ext JS, LLC.
14060  *
14061  * Originally Released Under LGPL - original licence link has changed is not relivant.
14062  *
14063  * Fork - LGPL
14064  * <script type="text/javascript">
14065  */
14066
14067  
14068 /**
14069  * @class Roo.util.CSS
14070  * Utility class for manipulating CSS rules
14071  * @singleton
14072  */
14073 Roo.util.CSS = function(){
14074         var rules = null;
14075         var doc = document;
14076
14077     var camelRe = /(-[a-z])/gi;
14078     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14079
14080    return {
14081    /**
14082     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14083     * tag and appended to the HEAD of the document.
14084     * @param {String|Object} cssText The text containing the css rules
14085     * @param {String} id An id to add to the stylesheet for later removal
14086     * @return {StyleSheet}
14087     */
14088     createStyleSheet : function(cssText, id){
14089         var ss;
14090         var head = doc.getElementsByTagName("head")[0];
14091         var nrules = doc.createElement("style");
14092         nrules.setAttribute("type", "text/css");
14093         if(id){
14094             nrules.setAttribute("id", id);
14095         }
14096         if (typeof(cssText) != 'string') {
14097             // support object maps..
14098             // not sure if this a good idea.. 
14099             // perhaps it should be merged with the general css handling
14100             // and handle js style props.
14101             var cssTextNew = [];
14102             for(var n in cssText) {
14103                 var citems = [];
14104                 for(var k in cssText[n]) {
14105                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14106                 }
14107                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14108                 
14109             }
14110             cssText = cssTextNew.join("\n");
14111             
14112         }
14113        
14114        
14115        if(Roo.isIE){
14116            head.appendChild(nrules);
14117            ss = nrules.styleSheet;
14118            ss.cssText = cssText;
14119        }else{
14120            try{
14121                 nrules.appendChild(doc.createTextNode(cssText));
14122            }catch(e){
14123                nrules.cssText = cssText; 
14124            }
14125            head.appendChild(nrules);
14126            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14127        }
14128        this.cacheStyleSheet(ss);
14129        return ss;
14130    },
14131
14132    /**
14133     * Removes a style or link tag by id
14134     * @param {String} id The id of the tag
14135     */
14136    removeStyleSheet : function(id){
14137        var existing = doc.getElementById(id);
14138        if(existing){
14139            existing.parentNode.removeChild(existing);
14140        }
14141    },
14142
14143    /**
14144     * Dynamically swaps an existing stylesheet reference for a new one
14145     * @param {String} id The id of an existing link tag to remove
14146     * @param {String} url The href of the new stylesheet to include
14147     */
14148    swapStyleSheet : function(id, url){
14149        this.removeStyleSheet(id);
14150        var ss = doc.createElement("link");
14151        ss.setAttribute("rel", "stylesheet");
14152        ss.setAttribute("type", "text/css");
14153        ss.setAttribute("id", id);
14154        ss.setAttribute("href", url);
14155        doc.getElementsByTagName("head")[0].appendChild(ss);
14156    },
14157    
14158    /**
14159     * Refresh the rule cache if you have dynamically added stylesheets
14160     * @return {Object} An object (hash) of rules indexed by selector
14161     */
14162    refreshCache : function(){
14163        return this.getRules(true);
14164    },
14165
14166    // private
14167    cacheStyleSheet : function(stylesheet){
14168        if(!rules){
14169            rules = {};
14170        }
14171        try{// try catch for cross domain access issue
14172            var ssRules = stylesheet.cssRules || stylesheet.rules;
14173            for(var j = ssRules.length-1; j >= 0; --j){
14174                rules[ssRules[j].selectorText] = ssRules[j];
14175            }
14176        }catch(e){}
14177    },
14178    
14179    /**
14180     * Gets all css rules for the document
14181     * @param {Boolean} refreshCache true to refresh the internal cache
14182     * @return {Object} An object (hash) of rules indexed by selector
14183     */
14184    getRules : function(refreshCache){
14185                 if(rules == null || refreshCache){
14186                         rules = {};
14187                         var ds = doc.styleSheets;
14188                         for(var i =0, len = ds.length; i < len; i++){
14189                             try{
14190                         this.cacheStyleSheet(ds[i]);
14191                     }catch(e){} 
14192                 }
14193                 }
14194                 return rules;
14195         },
14196         
14197         /**
14198     * Gets an an individual CSS rule by selector(s)
14199     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14200     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14201     * @return {CSSRule} The CSS rule or null if one is not found
14202     */
14203    getRule : function(selector, refreshCache){
14204                 var rs = this.getRules(refreshCache);
14205                 if(!(selector instanceof Array)){
14206                     return rs[selector];
14207                 }
14208                 for(var i = 0; i < selector.length; i++){
14209                         if(rs[selector[i]]){
14210                                 return rs[selector[i]];
14211                         }
14212                 }
14213                 return null;
14214         },
14215         
14216         
14217         /**
14218     * Updates a rule property
14219     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14220     * @param {String} property The css property
14221     * @param {String} value The new value for the property
14222     * @return {Boolean} true If a rule was found and updated
14223     */
14224    updateRule : function(selector, property, value){
14225                 if(!(selector instanceof Array)){
14226                         var rule = this.getRule(selector);
14227                         if(rule){
14228                                 rule.style[property.replace(camelRe, camelFn)] = value;
14229                                 return true;
14230                         }
14231                 }else{
14232                         for(var i = 0; i < selector.length; i++){
14233                                 if(this.updateRule(selector[i], property, value)){
14234                                         return true;
14235                                 }
14236                         }
14237                 }
14238                 return false;
14239         }
14240    };   
14241 }();/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252  
14253
14254 /**
14255  * @class Roo.util.ClickRepeater
14256  * @extends Roo.util.Observable
14257  * 
14258  * A wrapper class which can be applied to any element. Fires a "click" event while the
14259  * mouse is pressed. The interval between firings may be specified in the config but
14260  * defaults to 10 milliseconds.
14261  * 
14262  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14263  * 
14264  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14265  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14266  * Similar to an autorepeat key delay.
14267  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14268  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14269  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14270  *           "interval" and "delay" are ignored. "immediate" is honored.
14271  * @cfg {Boolean} preventDefault True to prevent the default click event
14272  * @cfg {Boolean} stopDefault True to stop the default click event
14273  * 
14274  * @history
14275  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14276  *     2007-02-02 jvs Renamed to ClickRepeater
14277  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14278  *
14279  *  @constructor
14280  * @param {String/HTMLElement/Element} el The element to listen on
14281  * @param {Object} config
14282  **/
14283 Roo.util.ClickRepeater = function(el, config)
14284 {
14285     this.el = Roo.get(el);
14286     this.el.unselectable();
14287
14288     Roo.apply(this, config);
14289
14290     this.addEvents({
14291     /**
14292      * @event mousedown
14293      * Fires when the mouse button is depressed.
14294      * @param {Roo.util.ClickRepeater} this
14295      */
14296         "mousedown" : true,
14297     /**
14298      * @event click
14299      * Fires on a specified interval during the time the element is pressed.
14300      * @param {Roo.util.ClickRepeater} this
14301      */
14302         "click" : true,
14303     /**
14304      * @event mouseup
14305      * Fires when the mouse key is released.
14306      * @param {Roo.util.ClickRepeater} this
14307      */
14308         "mouseup" : true
14309     });
14310
14311     this.el.on("mousedown", this.handleMouseDown, this);
14312     if(this.preventDefault || this.stopDefault){
14313         this.el.on("click", function(e){
14314             if(this.preventDefault){
14315                 e.preventDefault();
14316             }
14317             if(this.stopDefault){
14318                 e.stopEvent();
14319             }
14320         }, this);
14321     }
14322
14323     // allow inline handler
14324     if(this.handler){
14325         this.on("click", this.handler,  this.scope || this);
14326     }
14327
14328     Roo.util.ClickRepeater.superclass.constructor.call(this);
14329 };
14330
14331 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14332     interval : 20,
14333     delay: 250,
14334     preventDefault : true,
14335     stopDefault : false,
14336     timer : 0,
14337
14338     // private
14339     handleMouseDown : function(){
14340         clearTimeout(this.timer);
14341         this.el.blur();
14342         if(this.pressClass){
14343             this.el.addClass(this.pressClass);
14344         }
14345         this.mousedownTime = new Date();
14346
14347         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14348         this.el.on("mouseout", this.handleMouseOut, this);
14349
14350         this.fireEvent("mousedown", this);
14351         this.fireEvent("click", this);
14352         
14353         this.timer = this.click.defer(this.delay || this.interval, this);
14354     },
14355
14356     // private
14357     click : function(){
14358         this.fireEvent("click", this);
14359         this.timer = this.click.defer(this.getInterval(), this);
14360     },
14361
14362     // private
14363     getInterval: function(){
14364         if(!this.accelerate){
14365             return this.interval;
14366         }
14367         var pressTime = this.mousedownTime.getElapsed();
14368         if(pressTime < 500){
14369             return 400;
14370         }else if(pressTime < 1700){
14371             return 320;
14372         }else if(pressTime < 2600){
14373             return 250;
14374         }else if(pressTime < 3500){
14375             return 180;
14376         }else if(pressTime < 4400){
14377             return 140;
14378         }else if(pressTime < 5300){
14379             return 80;
14380         }else if(pressTime < 6200){
14381             return 50;
14382         }else{
14383             return 10;
14384         }
14385     },
14386
14387     // private
14388     handleMouseOut : function(){
14389         clearTimeout(this.timer);
14390         if(this.pressClass){
14391             this.el.removeClass(this.pressClass);
14392         }
14393         this.el.on("mouseover", this.handleMouseReturn, this);
14394     },
14395
14396     // private
14397     handleMouseReturn : function(){
14398         this.el.un("mouseover", this.handleMouseReturn);
14399         if(this.pressClass){
14400             this.el.addClass(this.pressClass);
14401         }
14402         this.click();
14403     },
14404
14405     // private
14406     handleMouseUp : function(){
14407         clearTimeout(this.timer);
14408         this.el.un("mouseover", this.handleMouseReturn);
14409         this.el.un("mouseout", this.handleMouseOut);
14410         Roo.get(document).un("mouseup", this.handleMouseUp);
14411         this.el.removeClass(this.pressClass);
14412         this.fireEvent("mouseup", this);
14413     }
14414 });/*
14415  * Based on:
14416  * Ext JS Library 1.1.1
14417  * Copyright(c) 2006-2007, Ext JS, LLC.
14418  *
14419  * Originally Released Under LGPL - original licence link has changed is not relivant.
14420  *
14421  * Fork - LGPL
14422  * <script type="text/javascript">
14423  */
14424
14425  
14426 /**
14427  * @class Roo.KeyNav
14428  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14429  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14430  * way to implement custom navigation schemes for any UI component.</p>
14431  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14432  * pageUp, pageDown, del, home, end.  Usage:</p>
14433  <pre><code>
14434 var nav = new Roo.KeyNav("my-element", {
14435     "left" : function(e){
14436         this.moveLeft(e.ctrlKey);
14437     },
14438     "right" : function(e){
14439         this.moveRight(e.ctrlKey);
14440     },
14441     "enter" : function(e){
14442         this.save();
14443     },
14444     scope : this
14445 });
14446 </code></pre>
14447  * @constructor
14448  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14449  * @param {Object} config The config
14450  */
14451 Roo.KeyNav = function(el, config){
14452     this.el = Roo.get(el);
14453     Roo.apply(this, config);
14454     if(!this.disabled){
14455         this.disabled = true;
14456         this.enable();
14457     }
14458 };
14459
14460 Roo.KeyNav.prototype = {
14461     /**
14462      * @cfg {Boolean} disabled
14463      * True to disable this KeyNav instance (defaults to false)
14464      */
14465     disabled : false,
14466     /**
14467      * @cfg {String} defaultEventAction
14468      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14469      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14470      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14471      */
14472     defaultEventAction: "stopEvent",
14473     /**
14474      * @cfg {Boolean} forceKeyDown
14475      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14476      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14477      * handle keydown instead of keypress.
14478      */
14479     forceKeyDown : false,
14480
14481     // private
14482     prepareEvent : function(e){
14483         var k = e.getKey();
14484         var h = this.keyToHandler[k];
14485         //if(h && this[h]){
14486         //    e.stopPropagation();
14487         //}
14488         if(Roo.isSafari && h && k >= 37 && k <= 40){
14489             e.stopEvent();
14490         }
14491     },
14492
14493     // private
14494     relay : function(e){
14495         var k = e.getKey();
14496         var h = this.keyToHandler[k];
14497         if(h && this[h]){
14498             if(this.doRelay(e, this[h], h) !== true){
14499                 e[this.defaultEventAction]();
14500             }
14501         }
14502     },
14503
14504     // private
14505     doRelay : function(e, h, hname){
14506         return h.call(this.scope || this, e);
14507     },
14508
14509     // possible handlers
14510     enter : false,
14511     left : false,
14512     right : false,
14513     up : false,
14514     down : false,
14515     tab : false,
14516     esc : false,
14517     pageUp : false,
14518     pageDown : false,
14519     del : false,
14520     home : false,
14521     end : false,
14522
14523     // quick lookup hash
14524     keyToHandler : {
14525         37 : "left",
14526         39 : "right",
14527         38 : "up",
14528         40 : "down",
14529         33 : "pageUp",
14530         34 : "pageDown",
14531         46 : "del",
14532         36 : "home",
14533         35 : "end",
14534         13 : "enter",
14535         27 : "esc",
14536         9  : "tab"
14537     },
14538
14539         /**
14540          * Enable this KeyNav
14541          */
14542         enable: function(){
14543                 if(this.disabled){
14544             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14545             // the EventObject will normalize Safari automatically
14546             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14547                 this.el.on("keydown", this.relay,  this);
14548             }else{
14549                 this.el.on("keydown", this.prepareEvent,  this);
14550                 this.el.on("keypress", this.relay,  this);
14551             }
14552                     this.disabled = false;
14553                 }
14554         },
14555
14556         /**
14557          * Disable this KeyNav
14558          */
14559         disable: function(){
14560                 if(!this.disabled){
14561                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14562                 this.el.un("keydown", this.relay);
14563             }else{
14564                 this.el.un("keydown", this.prepareEvent);
14565                 this.el.un("keypress", this.relay);
14566             }
14567                     this.disabled = true;
14568                 }
14569         }
14570 };/*
14571  * Based on:
14572  * Ext JS Library 1.1.1
14573  * Copyright(c) 2006-2007, Ext JS, LLC.
14574  *
14575  * Originally Released Under LGPL - original licence link has changed is not relivant.
14576  *
14577  * Fork - LGPL
14578  * <script type="text/javascript">
14579  */
14580
14581  
14582 /**
14583  * @class Roo.KeyMap
14584  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14585  * The constructor accepts the same config object as defined by {@link #addBinding}.
14586  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14587  * combination it will call the function with this signature (if the match is a multi-key
14588  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14589  * A KeyMap can also handle a string representation of keys.<br />
14590  * Usage:
14591  <pre><code>
14592 // map one key by key code
14593 var map = new Roo.KeyMap("my-element", {
14594     key: 13, // or Roo.EventObject.ENTER
14595     fn: myHandler,
14596     scope: myObject
14597 });
14598
14599 // map multiple keys to one action by string
14600 var map = new Roo.KeyMap("my-element", {
14601     key: "a\r\n\t",
14602     fn: myHandler,
14603     scope: myObject
14604 });
14605
14606 // map multiple keys to multiple actions by strings and array of codes
14607 var map = new Roo.KeyMap("my-element", [
14608     {
14609         key: [10,13],
14610         fn: function(){ alert("Return was pressed"); }
14611     }, {
14612         key: "abc",
14613         fn: function(){ alert('a, b or c was pressed'); }
14614     }, {
14615         key: "\t",
14616         ctrl:true,
14617         shift:true,
14618         fn: function(){ alert('Control + shift + tab was pressed.'); }
14619     }
14620 ]);
14621 </code></pre>
14622  * <b>Note: A KeyMap starts enabled</b>
14623  * @constructor
14624  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14625  * @param {Object} config The config (see {@link #addBinding})
14626  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14627  */
14628 Roo.KeyMap = function(el, config, eventName){
14629     this.el  = Roo.get(el);
14630     this.eventName = eventName || "keydown";
14631     this.bindings = [];
14632     if(config){
14633         this.addBinding(config);
14634     }
14635     this.enable();
14636 };
14637
14638 Roo.KeyMap.prototype = {
14639     /**
14640      * True to stop the event from bubbling and prevent the default browser action if the
14641      * key was handled by the KeyMap (defaults to false)
14642      * @type Boolean
14643      */
14644     stopEvent : false,
14645
14646     /**
14647      * Add a new binding to this KeyMap. The following config object properties are supported:
14648      * <pre>
14649 Property    Type             Description
14650 ----------  ---------------  ----------------------------------------------------------------------
14651 key         String/Array     A single keycode or an array of keycodes to handle
14652 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14653 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14654 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14655 fn          Function         The function to call when KeyMap finds the expected key combination
14656 scope       Object           The scope of the callback function
14657 </pre>
14658      *
14659      * Usage:
14660      * <pre><code>
14661 // Create a KeyMap
14662 var map = new Roo.KeyMap(document, {
14663     key: Roo.EventObject.ENTER,
14664     fn: handleKey,
14665     scope: this
14666 });
14667
14668 //Add a new binding to the existing KeyMap later
14669 map.addBinding({
14670     key: 'abc',
14671     shift: true,
14672     fn: handleKey,
14673     scope: this
14674 });
14675 </code></pre>
14676      * @param {Object/Array} config A single KeyMap config or an array of configs
14677      */
14678         addBinding : function(config){
14679         if(config instanceof Array){
14680             for(var i = 0, len = config.length; i < len; i++){
14681                 this.addBinding(config[i]);
14682             }
14683             return;
14684         }
14685         var keyCode = config.key,
14686             shift = config.shift, 
14687             ctrl = config.ctrl, 
14688             alt = config.alt,
14689             fn = config.fn,
14690             scope = config.scope;
14691         if(typeof keyCode == "string"){
14692             var ks = [];
14693             var keyString = keyCode.toUpperCase();
14694             for(var j = 0, len = keyString.length; j < len; j++){
14695                 ks.push(keyString.charCodeAt(j));
14696             }
14697             keyCode = ks;
14698         }
14699         var keyArray = keyCode instanceof Array;
14700         var handler = function(e){
14701             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14702                 var k = e.getKey();
14703                 if(keyArray){
14704                     for(var i = 0, len = keyCode.length; i < len; i++){
14705                         if(keyCode[i] == k){
14706                           if(this.stopEvent){
14707                               e.stopEvent();
14708                           }
14709                           fn.call(scope || window, k, e);
14710                           return;
14711                         }
14712                     }
14713                 }else{
14714                     if(k == keyCode){
14715                         if(this.stopEvent){
14716                            e.stopEvent();
14717                         }
14718                         fn.call(scope || window, k, e);
14719                     }
14720                 }
14721             }
14722         };
14723         this.bindings.push(handler);  
14724         },
14725
14726     /**
14727      * Shorthand for adding a single key listener
14728      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14729      * following options:
14730      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14731      * @param {Function} fn The function to call
14732      * @param {Object} scope (optional) The scope of the function
14733      */
14734     on : function(key, fn, scope){
14735         var keyCode, shift, ctrl, alt;
14736         if(typeof key == "object" && !(key instanceof Array)){
14737             keyCode = key.key;
14738             shift = key.shift;
14739             ctrl = key.ctrl;
14740             alt = key.alt;
14741         }else{
14742             keyCode = key;
14743         }
14744         this.addBinding({
14745             key: keyCode,
14746             shift: shift,
14747             ctrl: ctrl,
14748             alt: alt,
14749             fn: fn,
14750             scope: scope
14751         })
14752     },
14753
14754     // private
14755     handleKeyDown : function(e){
14756             if(this.enabled){ //just in case
14757             var b = this.bindings;
14758             for(var i = 0, len = b.length; i < len; i++){
14759                 b[i].call(this, e);
14760             }
14761             }
14762         },
14763         
14764         /**
14765          * Returns true if this KeyMap is enabled
14766          * @return {Boolean} 
14767          */
14768         isEnabled : function(){
14769             return this.enabled;  
14770         },
14771         
14772         /**
14773          * Enables this KeyMap
14774          */
14775         enable: function(){
14776                 if(!this.enabled){
14777                     this.el.on(this.eventName, this.handleKeyDown, this);
14778                     this.enabled = true;
14779                 }
14780         },
14781
14782         /**
14783          * Disable this KeyMap
14784          */
14785         disable: function(){
14786                 if(this.enabled){
14787                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14788                     this.enabled = false;
14789                 }
14790         }
14791 };/*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801
14802  
14803 /**
14804  * @class Roo.util.TextMetrics
14805  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14806  * wide, in pixels, a given block of text will be.
14807  * @singleton
14808  */
14809 Roo.util.TextMetrics = function(){
14810     var shared;
14811     return {
14812         /**
14813          * Measures the size of the specified text
14814          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14815          * that can affect the size of the rendered text
14816          * @param {String} text The text to measure
14817          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14818          * in order to accurately measure the text height
14819          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14820          */
14821         measure : function(el, text, fixedWidth){
14822             if(!shared){
14823                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14824             }
14825             shared.bind(el);
14826             shared.setFixedWidth(fixedWidth || 'auto');
14827             return shared.getSize(text);
14828         },
14829
14830         /**
14831          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14832          * the overhead of multiple calls to initialize the style properties on each measurement.
14833          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14834          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14835          * in order to accurately measure the text height
14836          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14837          */
14838         createInstance : function(el, fixedWidth){
14839             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14840         }
14841     };
14842 }();
14843
14844  
14845
14846 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14847     var ml = new Roo.Element(document.createElement('div'));
14848     document.body.appendChild(ml.dom);
14849     ml.position('absolute');
14850     ml.setLeftTop(-1000, -1000);
14851     ml.hide();
14852
14853     if(fixedWidth){
14854         ml.setWidth(fixedWidth);
14855     }
14856      
14857     var instance = {
14858         /**
14859          * Returns the size of the specified text based on the internal element's style and width properties
14860          * @memberOf Roo.util.TextMetrics.Instance#
14861          * @param {String} text The text to measure
14862          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14863          */
14864         getSize : function(text){
14865             ml.update(text);
14866             var s = ml.getSize();
14867             ml.update('');
14868             return s;
14869         },
14870
14871         /**
14872          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14873          * that can affect the size of the rendered text
14874          * @memberOf Roo.util.TextMetrics.Instance#
14875          * @param {String/HTMLElement} el The element, dom node or id
14876          */
14877         bind : function(el){
14878             ml.setStyle(
14879                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14880             );
14881         },
14882
14883         /**
14884          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14885          * to set a fixed width in order to accurately measure the text height.
14886          * @memberOf Roo.util.TextMetrics.Instance#
14887          * @param {Number} width The width to set on the element
14888          */
14889         setFixedWidth : function(width){
14890             ml.setWidth(width);
14891         },
14892
14893         /**
14894          * Returns the measured width of the specified text
14895          * @memberOf Roo.util.TextMetrics.Instance#
14896          * @param {String} text The text to measure
14897          * @return {Number} width The width in pixels
14898          */
14899         getWidth : function(text){
14900             ml.dom.style.width = 'auto';
14901             return this.getSize(text).width;
14902         },
14903
14904         /**
14905          * Returns the measured height of the specified text.  For multiline text, be sure to call
14906          * {@link #setFixedWidth} if necessary.
14907          * @memberOf Roo.util.TextMetrics.Instance#
14908          * @param {String} text The text to measure
14909          * @return {Number} height The height in pixels
14910          */
14911         getHeight : function(text){
14912             return this.getSize(text).height;
14913         }
14914     };
14915
14916     instance.bind(bindTo);
14917
14918     return instance;
14919 };
14920
14921 // backwards compat
14922 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932
14933 /**
14934  * @class Roo.state.Provider
14935  * Abstract base class for state provider implementations. This class provides methods
14936  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14937  * Provider interface.
14938  */
14939 Roo.state.Provider = function(){
14940     /**
14941      * @event statechange
14942      * Fires when a state change occurs.
14943      * @param {Provider} this This state provider
14944      * @param {String} key The state key which was changed
14945      * @param {String} value The encoded value for the state
14946      */
14947     this.addEvents({
14948         "statechange": true
14949     });
14950     this.state = {};
14951     Roo.state.Provider.superclass.constructor.call(this);
14952 };
14953 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14954     /**
14955      * Returns the current value for a key
14956      * @param {String} name The key name
14957      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14958      * @return {Mixed} The state data
14959      */
14960     get : function(name, defaultValue){
14961         return typeof this.state[name] == "undefined" ?
14962             defaultValue : this.state[name];
14963     },
14964     
14965     /**
14966      * Clears a value from the state
14967      * @param {String} name The key name
14968      */
14969     clear : function(name){
14970         delete this.state[name];
14971         this.fireEvent("statechange", this, name, null);
14972     },
14973     
14974     /**
14975      * Sets the value for a key
14976      * @param {String} name The key name
14977      * @param {Mixed} value The value to set
14978      */
14979     set : function(name, value){
14980         this.state[name] = value;
14981         this.fireEvent("statechange", this, name, value);
14982     },
14983     
14984     /**
14985      * Decodes a string previously encoded with {@link #encodeValue}.
14986      * @param {String} value The value to decode
14987      * @return {Mixed} The decoded value
14988      */
14989     decodeValue : function(cookie){
14990         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14991         var matches = re.exec(unescape(cookie));
14992         if(!matches || !matches[1]) {
14993             return; // non state cookie
14994         }
14995         var type = matches[1];
14996         var v = matches[2];
14997         switch(type){
14998             case "n":
14999                 return parseFloat(v);
15000             case "d":
15001                 return new Date(Date.parse(v));
15002             case "b":
15003                 return (v == "1");
15004             case "a":
15005                 var all = [];
15006                 var values = v.split("^");
15007                 for(var i = 0, len = values.length; i < len; i++){
15008                     all.push(this.decodeValue(values[i]));
15009                 }
15010                 return all;
15011            case "o":
15012                 var all = {};
15013                 var values = v.split("^");
15014                 for(var i = 0, len = values.length; i < len; i++){
15015                     var kv = values[i].split("=");
15016                     all[kv[0]] = this.decodeValue(kv[1]);
15017                 }
15018                 return all;
15019            default:
15020                 return v;
15021         }
15022     },
15023     
15024     /**
15025      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15026      * @param {Mixed} value The value to encode
15027      * @return {String} The encoded value
15028      */
15029     encodeValue : function(v){
15030         var enc;
15031         if(typeof v == "number"){
15032             enc = "n:" + v;
15033         }else if(typeof v == "boolean"){
15034             enc = "b:" + (v ? "1" : "0");
15035         }else if(v instanceof Date){
15036             enc = "d:" + v.toGMTString();
15037         }else if(v instanceof Array){
15038             var flat = "";
15039             for(var i = 0, len = v.length; i < len; i++){
15040                 flat += this.encodeValue(v[i]);
15041                 if(i != len-1) {
15042                     flat += "^";
15043                 }
15044             }
15045             enc = "a:" + flat;
15046         }else if(typeof v == "object"){
15047             var flat = "";
15048             for(var key in v){
15049                 if(typeof v[key] != "function"){
15050                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15051                 }
15052             }
15053             enc = "o:" + flat.substring(0, flat.length-1);
15054         }else{
15055             enc = "s:" + v;
15056         }
15057         return escape(enc);        
15058     }
15059 });
15060
15061 /*
15062  * Based on:
15063  * Ext JS Library 1.1.1
15064  * Copyright(c) 2006-2007, Ext JS, LLC.
15065  *
15066  * Originally Released Under LGPL - original licence link has changed is not relivant.
15067  *
15068  * Fork - LGPL
15069  * <script type="text/javascript">
15070  */
15071 /**
15072  * @class Roo.state.Manager
15073  * This is the global state manager. By default all components that are "state aware" check this class
15074  * for state information if you don't pass them a custom state provider. In order for this class
15075  * to be useful, it must be initialized with a provider when your application initializes.
15076  <pre><code>
15077 // in your initialization function
15078 init : function(){
15079    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15080    ...
15081    // supposed you have a {@link Roo.BorderLayout}
15082    var layout = new Roo.BorderLayout(...);
15083    layout.restoreState();
15084    // or a {Roo.BasicDialog}
15085    var dialog = new Roo.BasicDialog(...);
15086    dialog.restoreState();
15087  </code></pre>
15088  * @singleton
15089  */
15090 Roo.state.Manager = function(){
15091     var provider = new Roo.state.Provider();
15092     
15093     return {
15094         /**
15095          * Configures the default state provider for your application
15096          * @param {Provider} stateProvider The state provider to set
15097          */
15098         setProvider : function(stateProvider){
15099             provider = stateProvider;
15100         },
15101         
15102         /**
15103          * Returns the current value for a key
15104          * @param {String} name The key name
15105          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15106          * @return {Mixed} The state data
15107          */
15108         get : function(key, defaultValue){
15109             return provider.get(key, defaultValue);
15110         },
15111         
15112         /**
15113          * Sets the value for a key
15114          * @param {String} name The key name
15115          * @param {Mixed} value The state data
15116          */
15117          set : function(key, value){
15118             provider.set(key, value);
15119         },
15120         
15121         /**
15122          * Clears a value from the state
15123          * @param {String} name The key name
15124          */
15125         clear : function(key){
15126             provider.clear(key);
15127         },
15128         
15129         /**
15130          * Gets the currently configured state provider
15131          * @return {Provider} The state provider
15132          */
15133         getProvider : function(){
15134             return provider;
15135         }
15136     };
15137 }();
15138 /*
15139  * Based on:
15140  * Ext JS Library 1.1.1
15141  * Copyright(c) 2006-2007, Ext JS, LLC.
15142  *
15143  * Originally Released Under LGPL - original licence link has changed is not relivant.
15144  *
15145  * Fork - LGPL
15146  * <script type="text/javascript">
15147  */
15148 /**
15149  * @class Roo.state.CookieProvider
15150  * @extends Roo.state.Provider
15151  * The default Provider implementation which saves state via cookies.
15152  * <br />Usage:
15153  <pre><code>
15154    var cp = new Roo.state.CookieProvider({
15155        path: "/cgi-bin/",
15156        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15157        domain: "roojs.com"
15158    })
15159    Roo.state.Manager.setProvider(cp);
15160  </code></pre>
15161  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15162  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15163  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15164  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15165  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15166  * domain the page is running on including the 'www' like 'www.roojs.com')
15167  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15168  * @constructor
15169  * Create a new CookieProvider
15170  * @param {Object} config The configuration object
15171  */
15172 Roo.state.CookieProvider = function(config){
15173     Roo.state.CookieProvider.superclass.constructor.call(this);
15174     this.path = "/";
15175     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15176     this.domain = null;
15177     this.secure = false;
15178     Roo.apply(this, config);
15179     this.state = this.readCookies();
15180 };
15181
15182 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15183     // private
15184     set : function(name, value){
15185         if(typeof value == "undefined" || value === null){
15186             this.clear(name);
15187             return;
15188         }
15189         this.setCookie(name, value);
15190         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15191     },
15192
15193     // private
15194     clear : function(name){
15195         this.clearCookie(name);
15196         Roo.state.CookieProvider.superclass.clear.call(this, name);
15197     },
15198
15199     // private
15200     readCookies : function(){
15201         var cookies = {};
15202         var c = document.cookie + ";";
15203         var re = /\s?(.*?)=(.*?);/g;
15204         var matches;
15205         while((matches = re.exec(c)) != null){
15206             var name = matches[1];
15207             var value = matches[2];
15208             if(name && name.substring(0,3) == "ys-"){
15209                 cookies[name.substr(3)] = this.decodeValue(value);
15210             }
15211         }
15212         return cookies;
15213     },
15214
15215     // private
15216     setCookie : function(name, value){
15217         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15218            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15219            ((this.path == null) ? "" : ("; path=" + this.path)) +
15220            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15221            ((this.secure == true) ? "; secure" : "");
15222     },
15223
15224     // private
15225     clearCookie : function(name){
15226         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15227            ((this.path == null) ? "" : ("; path=" + this.path)) +
15228            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15229            ((this.secure == true) ? "; secure" : "");
15230     }
15231 });/*
15232  * Based on:
15233  * Ext JS Library 1.1.1
15234  * Copyright(c) 2006-2007, Ext JS, LLC.
15235  *
15236  * Originally Released Under LGPL - original licence link has changed is not relivant.
15237  *
15238  * Fork - LGPL
15239  * <script type="text/javascript">
15240  */
15241  
15242
15243 /**
15244  * @class Roo.ComponentMgr
15245  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15246  * @singleton
15247  */
15248 Roo.ComponentMgr = function(){
15249     var all = new Roo.util.MixedCollection();
15250
15251     return {
15252         /**
15253          * Registers a component.
15254          * @param {Roo.Component} c The component
15255          */
15256         register : function(c){
15257             all.add(c);
15258         },
15259
15260         /**
15261          * Unregisters a component.
15262          * @param {Roo.Component} c The component
15263          */
15264         unregister : function(c){
15265             all.remove(c);
15266         },
15267
15268         /**
15269          * Returns a component by id
15270          * @param {String} id The component id
15271          */
15272         get : function(id){
15273             return all.get(id);
15274         },
15275
15276         /**
15277          * Registers a function that will be called when a specified component is added to ComponentMgr
15278          * @param {String} id The component id
15279          * @param {Funtction} fn The callback function
15280          * @param {Object} scope The scope of the callback
15281          */
15282         onAvailable : function(id, fn, scope){
15283             all.on("add", function(index, o){
15284                 if(o.id == id){
15285                     fn.call(scope || o, o);
15286                     all.un("add", fn, scope);
15287                 }
15288             });
15289         }
15290     };
15291 }();/*
15292  * Based on:
15293  * Ext JS Library 1.1.1
15294  * Copyright(c) 2006-2007, Ext JS, LLC.
15295  *
15296  * Originally Released Under LGPL - original licence link has changed is not relivant.
15297  *
15298  * Fork - LGPL
15299  * <script type="text/javascript">
15300  */
15301  
15302 /**
15303  * @class Roo.Component
15304  * @extends Roo.util.Observable
15305  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15306  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15307  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15308  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15309  * All visual components (widgets) that require rendering into a layout should subclass Component.
15310  * @constructor
15311  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15312  * 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
15313  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15314  */
15315 Roo.Component = function(config){
15316     config = config || {};
15317     if(config.tagName || config.dom || typeof config == "string"){ // element object
15318         config = {el: config, id: config.id || config};
15319     }
15320     this.initialConfig = config;
15321
15322     Roo.apply(this, config);
15323     this.addEvents({
15324         /**
15325          * @event disable
15326          * Fires after the component is disabled.
15327              * @param {Roo.Component} this
15328              */
15329         disable : true,
15330         /**
15331          * @event enable
15332          * Fires after the component is enabled.
15333              * @param {Roo.Component} this
15334              */
15335         enable : true,
15336         /**
15337          * @event beforeshow
15338          * Fires before the component is shown.  Return false to stop the show.
15339              * @param {Roo.Component} this
15340              */
15341         beforeshow : true,
15342         /**
15343          * @event show
15344          * Fires after the component is shown.
15345              * @param {Roo.Component} this
15346              */
15347         show : true,
15348         /**
15349          * @event beforehide
15350          * Fires before the component is hidden. Return false to stop the hide.
15351              * @param {Roo.Component} this
15352              */
15353         beforehide : true,
15354         /**
15355          * @event hide
15356          * Fires after the component is hidden.
15357              * @param {Roo.Component} this
15358              */
15359         hide : true,
15360         /**
15361          * @event beforerender
15362          * Fires before the component is rendered. Return false to stop the render.
15363              * @param {Roo.Component} this
15364              */
15365         beforerender : true,
15366         /**
15367          * @event render
15368          * Fires after the component is rendered.
15369              * @param {Roo.Component} this
15370              */
15371         render : true,
15372         /**
15373          * @event beforedestroy
15374          * Fires before the component is destroyed. Return false to stop the destroy.
15375              * @param {Roo.Component} this
15376              */
15377         beforedestroy : true,
15378         /**
15379          * @event destroy
15380          * Fires after the component is destroyed.
15381              * @param {Roo.Component} this
15382              */
15383         destroy : true
15384     });
15385     if(!this.id){
15386         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15387     }
15388     Roo.ComponentMgr.register(this);
15389     Roo.Component.superclass.constructor.call(this);
15390     this.initComponent();
15391     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15392         this.render(this.renderTo);
15393         delete this.renderTo;
15394     }
15395 };
15396
15397 /** @private */
15398 Roo.Component.AUTO_ID = 1000;
15399
15400 Roo.extend(Roo.Component, Roo.util.Observable, {
15401     /**
15402      * @scope Roo.Component.prototype
15403      * @type {Boolean}
15404      * true if this component is hidden. Read-only.
15405      */
15406     hidden : false,
15407     /**
15408      * @type {Boolean}
15409      * true if this component is disabled. Read-only.
15410      */
15411     disabled : false,
15412     /**
15413      * @type {Boolean}
15414      * true if this component has been rendered. Read-only.
15415      */
15416     rendered : false,
15417     
15418     /** @cfg {String} disableClass
15419      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15420      */
15421     disabledClass : "x-item-disabled",
15422         /** @cfg {Boolean} allowDomMove
15423          * Whether the component can move the Dom node when rendering (defaults to true).
15424          */
15425     allowDomMove : true,
15426     /** @cfg {String} hideMode (display|visibility)
15427      * How this component should hidden. Supported values are
15428      * "visibility" (css visibility), "offsets" (negative offset position) and
15429      * "display" (css display) - defaults to "display".
15430      */
15431     hideMode: 'display',
15432
15433     /** @private */
15434     ctype : "Roo.Component",
15435
15436     /**
15437      * @cfg {String} actionMode 
15438      * which property holds the element that used for  hide() / show() / disable() / enable()
15439      * default is 'el' 
15440      */
15441     actionMode : "el",
15442
15443     /** @private */
15444     getActionEl : function(){
15445         return this[this.actionMode];
15446     },
15447
15448     initComponent : Roo.emptyFn,
15449     /**
15450      * If this is a lazy rendering component, render it to its container element.
15451      * @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.
15452      */
15453     render : function(container, position){
15454         
15455         if(this.rendered){
15456             return this;
15457         }
15458         
15459         if(this.fireEvent("beforerender", this) === false){
15460             return false;
15461         }
15462         
15463         if(!container && this.el){
15464             this.el = Roo.get(this.el);
15465             container = this.el.dom.parentNode;
15466             this.allowDomMove = false;
15467         }
15468         this.container = Roo.get(container);
15469         this.rendered = true;
15470         if(position !== undefined){
15471             if(typeof position == 'number'){
15472                 position = this.container.dom.childNodes[position];
15473             }else{
15474                 position = Roo.getDom(position);
15475             }
15476         }
15477         this.onRender(this.container, position || null);
15478         if(this.cls){
15479             this.el.addClass(this.cls);
15480             delete this.cls;
15481         }
15482         if(this.style){
15483             this.el.applyStyles(this.style);
15484             delete this.style;
15485         }
15486         this.fireEvent("render", this);
15487         this.afterRender(this.container);
15488         if(this.hidden){
15489             this.hide();
15490         }
15491         if(this.disabled){
15492             this.disable();
15493         }
15494
15495         return this;
15496         
15497     },
15498
15499     /** @private */
15500     // default function is not really useful
15501     onRender : function(ct, position){
15502         if(this.el){
15503             this.el = Roo.get(this.el);
15504             if(this.allowDomMove !== false){
15505                 ct.dom.insertBefore(this.el.dom, position);
15506             }
15507         }
15508     },
15509
15510     /** @private */
15511     getAutoCreate : function(){
15512         var cfg = typeof this.autoCreate == "object" ?
15513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15514         if(this.id && !cfg.id){
15515             cfg.id = this.id;
15516         }
15517         return cfg;
15518     },
15519
15520     /** @private */
15521     afterRender : Roo.emptyFn,
15522
15523     /**
15524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15526      */
15527     destroy : function(){
15528         if(this.fireEvent("beforedestroy", this) !== false){
15529             this.purgeListeners();
15530             this.beforeDestroy();
15531             if(this.rendered){
15532                 this.el.removeAllListeners();
15533                 this.el.remove();
15534                 if(this.actionMode == "container"){
15535                     this.container.remove();
15536                 }
15537             }
15538             this.onDestroy();
15539             Roo.ComponentMgr.unregister(this);
15540             this.fireEvent("destroy", this);
15541         }
15542     },
15543
15544         /** @private */
15545     beforeDestroy : function(){
15546
15547     },
15548
15549         /** @private */
15550         onDestroy : function(){
15551
15552     },
15553
15554     /**
15555      * Returns the underlying {@link Roo.Element}.
15556      * @return {Roo.Element} The element
15557      */
15558     getEl : function(){
15559         return this.el;
15560     },
15561
15562     /**
15563      * Returns the id of this component.
15564      * @return {String}
15565      */
15566     getId : function(){
15567         return this.id;
15568     },
15569
15570     /**
15571      * Try to focus this component.
15572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15573      * @return {Roo.Component} this
15574      */
15575     focus : function(selectText){
15576         if(this.rendered){
15577             this.el.focus();
15578             if(selectText === true){
15579                 this.el.dom.select();
15580             }
15581         }
15582         return this;
15583     },
15584
15585     /** @private */
15586     blur : function(){
15587         if(this.rendered){
15588             this.el.blur();
15589         }
15590         return this;
15591     },
15592
15593     /**
15594      * Disable this component.
15595      * @return {Roo.Component} this
15596      */
15597     disable : function(){
15598         if(this.rendered){
15599             this.onDisable();
15600         }
15601         this.disabled = true;
15602         this.fireEvent("disable", this);
15603         return this;
15604     },
15605
15606         // private
15607     onDisable : function(){
15608         this.getActionEl().addClass(this.disabledClass);
15609         this.el.dom.disabled = true;
15610     },
15611
15612     /**
15613      * Enable this component.
15614      * @return {Roo.Component} this
15615      */
15616     enable : function(){
15617         if(this.rendered){
15618             this.onEnable();
15619         }
15620         this.disabled = false;
15621         this.fireEvent("enable", this);
15622         return this;
15623     },
15624
15625         // private
15626     onEnable : function(){
15627         this.getActionEl().removeClass(this.disabledClass);
15628         this.el.dom.disabled = false;
15629     },
15630
15631     /**
15632      * Convenience function for setting disabled/enabled by boolean.
15633      * @param {Boolean} disabled
15634      */
15635     setDisabled : function(disabled){
15636         this[disabled ? "disable" : "enable"]();
15637     },
15638
15639     /**
15640      * Show this component.
15641      * @return {Roo.Component} this
15642      */
15643     show: function(){
15644         if(this.fireEvent("beforeshow", this) !== false){
15645             this.hidden = false;
15646             if(this.rendered){
15647                 this.onShow();
15648             }
15649             this.fireEvent("show", this);
15650         }
15651         return this;
15652     },
15653
15654     // private
15655     onShow : function(){
15656         var ae = this.getActionEl();
15657         if(this.hideMode == 'visibility'){
15658             ae.dom.style.visibility = "visible";
15659         }else if(this.hideMode == 'offsets'){
15660             ae.removeClass('x-hidden');
15661         }else{
15662             ae.dom.style.display = "";
15663         }
15664     },
15665
15666     /**
15667      * Hide this component.
15668      * @return {Roo.Component} this
15669      */
15670     hide: function(){
15671         if(this.fireEvent("beforehide", this) !== false){
15672             this.hidden = true;
15673             if(this.rendered){
15674                 this.onHide();
15675             }
15676             this.fireEvent("hide", this);
15677         }
15678         return this;
15679     },
15680
15681     // private
15682     onHide : function(){
15683         var ae = this.getActionEl();
15684         if(this.hideMode == 'visibility'){
15685             ae.dom.style.visibility = "hidden";
15686         }else if(this.hideMode == 'offsets'){
15687             ae.addClass('x-hidden');
15688         }else{
15689             ae.dom.style.display = "none";
15690         }
15691     },
15692
15693     /**
15694      * Convenience function to hide or show this component by boolean.
15695      * @param {Boolean} visible True to show, false to hide
15696      * @return {Roo.Component} this
15697      */
15698     setVisible: function(visible){
15699         if(visible) {
15700             this.show();
15701         }else{
15702             this.hide();
15703         }
15704         return this;
15705     },
15706
15707     /**
15708      * Returns true if this component is visible.
15709      */
15710     isVisible : function(){
15711         return this.getActionEl().isVisible();
15712     },
15713
15714     cloneConfig : function(overrides){
15715         overrides = overrides || {};
15716         var id = overrides.id || Roo.id();
15717         var cfg = Roo.applyIf(overrides, this.initialConfig);
15718         cfg.id = id; // prevent dup id
15719         return new this.constructor(cfg);
15720     }
15721 });/*
15722  * Based on:
15723  * Ext JS Library 1.1.1
15724  * Copyright(c) 2006-2007, Ext JS, LLC.
15725  *
15726  * Originally Released Under LGPL - original licence link has changed is not relivant.
15727  *
15728  * Fork - LGPL
15729  * <script type="text/javascript">
15730  */
15731
15732 /**
15733  * @class Roo.BoxComponent
15734  * @extends Roo.Component
15735  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15736  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15737  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15738  * layout containers.
15739  * @constructor
15740  * @param {Roo.Element/String/Object} config The configuration options.
15741  */
15742 Roo.BoxComponent = function(config){
15743     Roo.Component.call(this, config);
15744     this.addEvents({
15745         /**
15746          * @event resize
15747          * Fires after the component is resized.
15748              * @param {Roo.Component} this
15749              * @param {Number} adjWidth The box-adjusted width that was set
15750              * @param {Number} adjHeight The box-adjusted height that was set
15751              * @param {Number} rawWidth The width that was originally specified
15752              * @param {Number} rawHeight The height that was originally specified
15753              */
15754         resize : true,
15755         /**
15756          * @event move
15757          * Fires after the component is moved.
15758              * @param {Roo.Component} this
15759              * @param {Number} x The new x position
15760              * @param {Number} y The new y position
15761              */
15762         move : true
15763     });
15764 };
15765
15766 Roo.extend(Roo.BoxComponent, Roo.Component, {
15767     // private, set in afterRender to signify that the component has been rendered
15768     boxReady : false,
15769     // private, used to defer height settings to subclasses
15770     deferHeight: false,
15771     /** @cfg {Number} width
15772      * width (optional) size of component
15773      */
15774      /** @cfg {Number} height
15775      * height (optional) size of component
15776      */
15777      
15778     /**
15779      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15780      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15781      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15782      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15783      * @return {Roo.BoxComponent} this
15784      */
15785     setSize : function(w, h){
15786         // support for standard size objects
15787         if(typeof w == 'object'){
15788             h = w.height;
15789             w = w.width;
15790         }
15791         // not rendered
15792         if(!this.boxReady){
15793             this.width = w;
15794             this.height = h;
15795             return this;
15796         }
15797
15798         // prevent recalcs when not needed
15799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15800             return this;
15801         }
15802         this.lastSize = {width: w, height: h};
15803
15804         var adj = this.adjustSize(w, h);
15805         var aw = adj.width, ah = adj.height;
15806         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15807             var rz = this.getResizeEl();
15808             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15809                 rz.setSize(aw, ah);
15810             }else if(!this.deferHeight && ah !== undefined){
15811                 rz.setHeight(ah);
15812             }else if(aw !== undefined){
15813                 rz.setWidth(aw);
15814             }
15815             this.onResize(aw, ah, w, h);
15816             this.fireEvent('resize', this, aw, ah, w, h);
15817         }
15818         return this;
15819     },
15820
15821     /**
15822      * Gets the current size of the component's underlying element.
15823      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15824      */
15825     getSize : function(){
15826         return this.el.getSize();
15827     },
15828
15829     /**
15830      * Gets the current XY position of the component's underlying element.
15831      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15832      * @return {Array} The XY position of the element (e.g., [100, 200])
15833      */
15834     getPosition : function(local){
15835         if(local === true){
15836             return [this.el.getLeft(true), this.el.getTop(true)];
15837         }
15838         return this.xy || this.el.getXY();
15839     },
15840
15841     /**
15842      * Gets the current box measurements of the component's underlying element.
15843      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15844      * @returns {Object} box An object in the format {x, y, width, height}
15845      */
15846     getBox : function(local){
15847         var s = this.el.getSize();
15848         if(local){
15849             s.x = this.el.getLeft(true);
15850             s.y = this.el.getTop(true);
15851         }else{
15852             var xy = this.xy || this.el.getXY();
15853             s.x = xy[0];
15854             s.y = xy[1];
15855         }
15856         return s;
15857     },
15858
15859     /**
15860      * Sets the current box measurements of the component's underlying element.
15861      * @param {Object} box An object in the format {x, y, width, height}
15862      * @returns {Roo.BoxComponent} this
15863      */
15864     updateBox : function(box){
15865         this.setSize(box.width, box.height);
15866         this.setPagePosition(box.x, box.y);
15867         return this;
15868     },
15869
15870     // protected
15871     getResizeEl : function(){
15872         return this.resizeEl || this.el;
15873     },
15874
15875     // protected
15876     getPositionEl : function(){
15877         return this.positionEl || this.el;
15878     },
15879
15880     /**
15881      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15882      * This method fires the move event.
15883      * @param {Number} left The new left
15884      * @param {Number} top The new top
15885      * @returns {Roo.BoxComponent} this
15886      */
15887     setPosition : function(x, y){
15888         this.x = x;
15889         this.y = y;
15890         if(!this.boxReady){
15891             return this;
15892         }
15893         var adj = this.adjustPosition(x, y);
15894         var ax = adj.x, ay = adj.y;
15895
15896         var el = this.getPositionEl();
15897         if(ax !== undefined || ay !== undefined){
15898             if(ax !== undefined && ay !== undefined){
15899                 el.setLeftTop(ax, ay);
15900             }else if(ax !== undefined){
15901                 el.setLeft(ax);
15902             }else if(ay !== undefined){
15903                 el.setTop(ay);
15904             }
15905             this.onPosition(ax, ay);
15906             this.fireEvent('move', this, ax, ay);
15907         }
15908         return this;
15909     },
15910
15911     /**
15912      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15913      * This method fires the move event.
15914      * @param {Number} x The new x position
15915      * @param {Number} y The new y position
15916      * @returns {Roo.BoxComponent} this
15917      */
15918     setPagePosition : function(x, y){
15919         this.pageX = x;
15920         this.pageY = y;
15921         if(!this.boxReady){
15922             return;
15923         }
15924         if(x === undefined || y === undefined){ // cannot translate undefined points
15925             return;
15926         }
15927         var p = this.el.translatePoints(x, y);
15928         this.setPosition(p.left, p.top);
15929         return this;
15930     },
15931
15932     // private
15933     onRender : function(ct, position){
15934         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15935         if(this.resizeEl){
15936             this.resizeEl = Roo.get(this.resizeEl);
15937         }
15938         if(this.positionEl){
15939             this.positionEl = Roo.get(this.positionEl);
15940         }
15941     },
15942
15943     // private
15944     afterRender : function(){
15945         Roo.BoxComponent.superclass.afterRender.call(this);
15946         this.boxReady = true;
15947         this.setSize(this.width, this.height);
15948         if(this.x || this.y){
15949             this.setPosition(this.x, this.y);
15950         }
15951         if(this.pageX || this.pageY){
15952             this.setPagePosition(this.pageX, this.pageY);
15953         }
15954     },
15955
15956     /**
15957      * Force the component's size to recalculate based on the underlying element's current height and width.
15958      * @returns {Roo.BoxComponent} this
15959      */
15960     syncSize : function(){
15961         delete this.lastSize;
15962         this.setSize(this.el.getWidth(), this.el.getHeight());
15963         return this;
15964     },
15965
15966     /**
15967      * Called after the component is resized, this method is empty by default but can be implemented by any
15968      * subclass that needs to perform custom logic after a resize occurs.
15969      * @param {Number} adjWidth The box-adjusted width that was set
15970      * @param {Number} adjHeight The box-adjusted height that was set
15971      * @param {Number} rawWidth The width that was originally specified
15972      * @param {Number} rawHeight The height that was originally specified
15973      */
15974     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15975
15976     },
15977
15978     /**
15979      * Called after the component is moved, this method is empty by default but can be implemented by any
15980      * subclass that needs to perform custom logic after a move occurs.
15981      * @param {Number} x The new x position
15982      * @param {Number} y The new y position
15983      */
15984     onPosition : function(x, y){
15985
15986     },
15987
15988     // private
15989     adjustSize : function(w, h){
15990         if(this.autoWidth){
15991             w = 'auto';
15992         }
15993         if(this.autoHeight){
15994             h = 'auto';
15995         }
15996         return {width : w, height: h};
15997     },
15998
15999     // private
16000     adjustPosition : function(x, y){
16001         return {x : x, y: y};
16002     }
16003 });/*
16004  * Original code for Roojs - LGPL
16005  * <script type="text/javascript">
16006  */
16007  
16008 /**
16009  * @class Roo.XComponent
16010  * A delayed Element creator...
16011  * Or a way to group chunks of interface together.
16012  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16013  *  used in conjunction with XComponent.build() it will create an instance of each element,
16014  *  then call addxtype() to build the User interface.
16015  * 
16016  * Mypart.xyx = new Roo.XComponent({
16017
16018     parent : 'Mypart.xyz', // empty == document.element.!!
16019     order : '001',
16020     name : 'xxxx'
16021     region : 'xxxx'
16022     disabled : function() {} 
16023      
16024     tree : function() { // return an tree of xtype declared components
16025         var MODULE = this;
16026         return 
16027         {
16028             xtype : 'NestedLayoutPanel',
16029             // technicall
16030         }
16031      ]
16032  *})
16033  *
16034  *
16035  * It can be used to build a big heiracy, with parent etc.
16036  * or you can just use this to render a single compoent to a dom element
16037  * MYPART.render(Roo.Element | String(id) | dom_element )
16038  *
16039  *
16040  * Usage patterns.
16041  *
16042  * Classic Roo
16043  *
16044  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16045  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16046  *
16047  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16048  *
16049  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16050  * - if mulitple topModules exist, the last one is defined as the top module.
16051  *
16052  * Embeded Roo
16053  * 
16054  * When the top level or multiple modules are to embedded into a existing HTML page,
16055  * the parent element can container '#id' of the element where the module will be drawn.
16056  *
16057  * Bootstrap Roo
16058  *
16059  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16060  * it relies more on a include mechanism, where sub modules are included into an outer page.
16061  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16062  * 
16063  * Bootstrap Roo Included elements
16064  *
16065  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16066  * hence confusing the component builder as it thinks there are multiple top level elements. 
16067  *
16068  * String Over-ride & Translations
16069  *
16070  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16071  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16072  * are needed. @see Roo.XComponent.overlayString  
16073  * 
16074  * 
16075  * 
16076  * @extends Roo.util.Observable
16077  * @constructor
16078  * @param cfg {Object} configuration of component
16079  * 
16080  */
16081 Roo.XComponent = function(cfg) {
16082     Roo.apply(this, cfg);
16083     this.addEvents({ 
16084         /**
16085              * @event built
16086              * Fires when this the componnt is built
16087              * @param {Roo.XComponent} c the component
16088              */
16089         'built' : true
16090         
16091     });
16092     this.region = this.region || 'center'; // default..
16093     Roo.XComponent.register(this);
16094     this.modules = false;
16095     this.el = false; // where the layout goes..
16096     
16097     
16098 }
16099 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16100     /**
16101      * @property el
16102      * The created element (with Roo.factory())
16103      * @type {Roo.Layout}
16104      */
16105     el  : false,
16106     
16107     /**
16108      * @property el
16109      * for BC  - use el in new code
16110      * @type {Roo.Layout}
16111      */
16112     panel : false,
16113     
16114     /**
16115      * @property layout
16116      * for BC  - use el in new code
16117      * @type {Roo.Layout}
16118      */
16119     layout : false,
16120     
16121      /**
16122      * @cfg {Function|boolean} disabled
16123      * If this module is disabled by some rule, return true from the funtion
16124      */
16125     disabled : false,
16126     
16127     /**
16128      * @cfg {String} parent 
16129      * Name of parent element which it get xtype added to..
16130      */
16131     parent: false,
16132     
16133     /**
16134      * @cfg {String} order
16135      * Used to set the order in which elements are created (usefull for multiple tabs)
16136      */
16137     
16138     order : false,
16139     /**
16140      * @cfg {String} name
16141      * String to display while loading.
16142      */
16143     name : false,
16144     /**
16145      * @cfg {String} region
16146      * Region to render component to (defaults to center)
16147      */
16148     region : 'center',
16149     
16150     /**
16151      * @cfg {Array} items
16152      * A single item array - the first element is the root of the tree..
16153      * It's done this way to stay compatible with the Xtype system...
16154      */
16155     items : false,
16156     
16157     /**
16158      * @property _tree
16159      * The method that retuns the tree of parts that make up this compoennt 
16160      * @type {function}
16161      */
16162     _tree  : false,
16163     
16164      /**
16165      * render
16166      * render element to dom or tree
16167      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16168      */
16169     
16170     render : function(el)
16171     {
16172         
16173         el = el || false;
16174         var hp = this.parent ? 1 : 0;
16175         Roo.debug &&  Roo.log(this);
16176         
16177         var tree = this._tree ? this._tree() : this.tree();
16178
16179         
16180         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16181             // if parent is a '#.....' string, then let's use that..
16182             var ename = this.parent.substr(1);
16183             this.parent = false;
16184             Roo.debug && Roo.log(ename);
16185             switch (ename) {
16186                 case 'bootstrap-body':
16187                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16188                         // this is the BorderLayout standard?
16189                        this.parent = { el : true };
16190                        break;
16191                     }
16192                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16193                         // need to insert stuff...
16194                         this.parent =  {
16195                              el : new Roo.bootstrap.layout.Border({
16196                                  el : document.body, 
16197                      
16198                                  center: {
16199                                     titlebar: false,
16200                                     autoScroll:false,
16201                                     closeOnTab: true,
16202                                     tabPosition: 'top',
16203                                       //resizeTabs: true,
16204                                     alwaysShowTabs: true,
16205                                     hideTabs: false
16206                                      //minTabWidth: 140
16207                                  }
16208                              })
16209                         
16210                          };
16211                          break;
16212                     }
16213                          
16214                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16215                         this.parent = { el :  new  Roo.bootstrap.Body() };
16216                         Roo.debug && Roo.log("setting el to doc body");
16217                          
16218                     } else {
16219                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16220                     }
16221                     break;
16222                 case 'bootstrap':
16223                     this.parent = { el : true};
16224                     // fall through
16225                 default:
16226                     el = Roo.get(ename);
16227                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16228                         this.parent = { el : true};
16229                     }
16230                     
16231                     break;
16232             }
16233                 
16234             
16235             if (!el && !this.parent) {
16236                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16237                 return;
16238             }
16239         }
16240         
16241         Roo.debug && Roo.log("EL:");
16242         Roo.debug && Roo.log(el);
16243         Roo.debug && Roo.log("this.parent.el:");
16244         Roo.debug && Roo.log(this.parent.el);
16245         
16246
16247         // altertive root elements ??? - we need a better way to indicate these.
16248         var is_alt = Roo.XComponent.is_alt ||
16249                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16250                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16251                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16252         
16253         
16254         
16255         if (!this.parent && is_alt) {
16256             //el = Roo.get(document.body);
16257             this.parent = { el : true };
16258         }
16259             
16260             
16261         
16262         if (!this.parent) {
16263             
16264             Roo.debug && Roo.log("no parent - creating one");
16265             
16266             el = el ? Roo.get(el) : false;      
16267             
16268             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16269                 
16270                 this.parent =  {
16271                     el : new Roo.bootstrap.layout.Border({
16272                         el: el || document.body,
16273                     
16274                         center: {
16275                             titlebar: false,
16276                             autoScroll:false,
16277                             closeOnTab: true,
16278                             tabPosition: 'top',
16279                              //resizeTabs: true,
16280                             alwaysShowTabs: false,
16281                             hideTabs: true,
16282                             minTabWidth: 140,
16283                             overflow: 'visible'
16284                          }
16285                      })
16286                 };
16287             } else {
16288             
16289                 // it's a top level one..
16290                 this.parent =  {
16291                     el : new Roo.BorderLayout(el || document.body, {
16292                         center: {
16293                             titlebar: false,
16294                             autoScroll:false,
16295                             closeOnTab: true,
16296                             tabPosition: 'top',
16297                              //resizeTabs: true,
16298                             alwaysShowTabs: el && hp? false :  true,
16299                             hideTabs: el || !hp ? true :  false,
16300                             minTabWidth: 140
16301                          }
16302                     })
16303                 };
16304             }
16305         }
16306         
16307         if (!this.parent.el) {
16308                 // probably an old style ctor, which has been disabled.
16309                 return;
16310
16311         }
16312                 // The 'tree' method is  '_tree now' 
16313             
16314         tree.region = tree.region || this.region;
16315         var is_body = false;
16316         if (this.parent.el === true) {
16317             // bootstrap... - body..
16318             if (el) {
16319                 tree.el = el;
16320             }
16321             this.parent.el = Roo.factory(tree);
16322             is_body = true;
16323         }
16324         
16325         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16326         this.fireEvent('built', this);
16327         
16328         this.panel = this.el;
16329         this.layout = this.panel.layout;
16330         this.parentLayout = this.parent.layout  || false;  
16331          
16332     }
16333     
16334 });
16335
16336 Roo.apply(Roo.XComponent, {
16337     /**
16338      * @property  hideProgress
16339      * true to disable the building progress bar.. usefull on single page renders.
16340      * @type Boolean
16341      */
16342     hideProgress : false,
16343     /**
16344      * @property  buildCompleted
16345      * True when the builder has completed building the interface.
16346      * @type Boolean
16347      */
16348     buildCompleted : false,
16349      
16350     /**
16351      * @property  topModule
16352      * the upper most module - uses document.element as it's constructor.
16353      * @type Object
16354      */
16355      
16356     topModule  : false,
16357       
16358     /**
16359      * @property  modules
16360      * array of modules to be created by registration system.
16361      * @type {Array} of Roo.XComponent
16362      */
16363     
16364     modules : [],
16365     /**
16366      * @property  elmodules
16367      * array of modules to be created by which use #ID 
16368      * @type {Array} of Roo.XComponent
16369      */
16370      
16371     elmodules : [],
16372
16373      /**
16374      * @property  is_alt
16375      * Is an alternative Root - normally used by bootstrap or other systems,
16376      *    where the top element in the tree can wrap 'body' 
16377      * @type {boolean}  (default false)
16378      */
16379      
16380     is_alt : false,
16381     /**
16382      * @property  build_from_html
16383      * Build elements from html - used by bootstrap HTML stuff 
16384      *    - this is cleared after build is completed
16385      * @type {boolean}    (default false)
16386      */
16387      
16388     build_from_html : false,
16389     /**
16390      * Register components to be built later.
16391      *
16392      * This solves the following issues
16393      * - Building is not done on page load, but after an authentication process has occured.
16394      * - Interface elements are registered on page load
16395      * - Parent Interface elements may not be loaded before child, so this handles that..
16396      * 
16397      *
16398      * example:
16399      * 
16400      * MyApp.register({
16401           order : '000001',
16402           module : 'Pman.Tab.projectMgr',
16403           region : 'center',
16404           parent : 'Pman.layout',
16405           disabled : false,  // or use a function..
16406         })
16407      
16408      * * @param {Object} details about module
16409      */
16410     register : function(obj) {
16411                 
16412         Roo.XComponent.event.fireEvent('register', obj);
16413         switch(typeof(obj.disabled) ) {
16414                 
16415             case 'undefined':
16416                 break;
16417             
16418             case 'function':
16419                 if ( obj.disabled() ) {
16420                         return;
16421                 }
16422                 break;
16423             
16424             default:
16425                 if (obj.disabled || obj.region == '#disabled') {
16426                         return;
16427                 }
16428                 break;
16429         }
16430                 
16431         this.modules.push(obj);
16432          
16433     },
16434     /**
16435      * convert a string to an object..
16436      * eg. 'AAA.BBB' -> finds AAA.BBB
16437
16438      */
16439     
16440     toObject : function(str)
16441     {
16442         if (!str || typeof(str) == 'object') {
16443             return str;
16444         }
16445         if (str.substring(0,1) == '#') {
16446             return str;
16447         }
16448
16449         var ar = str.split('.');
16450         var rt, o;
16451         rt = ar.shift();
16452             /** eval:var:o */
16453         try {
16454             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16455         } catch (e) {
16456             throw "Module not found : " + str;
16457         }
16458         
16459         if (o === false) {
16460             throw "Module not found : " + str;
16461         }
16462         Roo.each(ar, function(e) {
16463             if (typeof(o[e]) == 'undefined') {
16464                 throw "Module not found : " + str;
16465             }
16466             o = o[e];
16467         });
16468         
16469         return o;
16470         
16471     },
16472     
16473     
16474     /**
16475      * move modules into their correct place in the tree..
16476      * 
16477      */
16478     preBuild : function ()
16479     {
16480         var _t = this;
16481         Roo.each(this.modules , function (obj)
16482         {
16483             Roo.XComponent.event.fireEvent('beforebuild', obj);
16484             
16485             var opar = obj.parent;
16486             try { 
16487                 obj.parent = this.toObject(opar);
16488             } catch(e) {
16489                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16490                 return;
16491             }
16492             
16493             if (!obj.parent) {
16494                 Roo.debug && Roo.log("GOT top level module");
16495                 Roo.debug && Roo.log(obj);
16496                 obj.modules = new Roo.util.MixedCollection(false, 
16497                     function(o) { return o.order + '' }
16498                 );
16499                 this.topModule = obj;
16500                 return;
16501             }
16502                         // parent is a string (usually a dom element name..)
16503             if (typeof(obj.parent) == 'string') {
16504                 this.elmodules.push(obj);
16505                 return;
16506             }
16507             if (obj.parent.constructor != Roo.XComponent) {
16508                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16509             }
16510             if (!obj.parent.modules) {
16511                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16512                     function(o) { return o.order + '' }
16513                 );
16514             }
16515             if (obj.parent.disabled) {
16516                 obj.disabled = true;
16517             }
16518             obj.parent.modules.add(obj);
16519         }, this);
16520     },
16521     
16522      /**
16523      * make a list of modules to build.
16524      * @return {Array} list of modules. 
16525      */ 
16526     
16527     buildOrder : function()
16528     {
16529         var _this = this;
16530         var cmp = function(a,b) {   
16531             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16532         };
16533         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16534             throw "No top level modules to build";
16535         }
16536         
16537         // make a flat list in order of modules to build.
16538         var mods = this.topModule ? [ this.topModule ] : [];
16539                 
16540         
16541         // elmodules (is a list of DOM based modules )
16542         Roo.each(this.elmodules, function(e) {
16543             mods.push(e);
16544             if (!this.topModule &&
16545                 typeof(e.parent) == 'string' &&
16546                 e.parent.substring(0,1) == '#' &&
16547                 Roo.get(e.parent.substr(1))
16548                ) {
16549                 
16550                 _this.topModule = e;
16551             }
16552             
16553         });
16554
16555         
16556         // add modules to their parents..
16557         var addMod = function(m) {
16558             Roo.debug && Roo.log("build Order: add: " + m.name);
16559                 
16560             mods.push(m);
16561             if (m.modules && !m.disabled) {
16562                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16563                 m.modules.keySort('ASC',  cmp );
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16565     
16566                 m.modules.each(addMod);
16567             } else {
16568                 Roo.debug && Roo.log("build Order: no child modules");
16569             }
16570             // not sure if this is used any more..
16571             if (m.finalize) {
16572                 m.finalize.name = m.name + " (clean up) ";
16573                 mods.push(m.finalize);
16574             }
16575             
16576         }
16577         if (this.topModule && this.topModule.modules) { 
16578             this.topModule.modules.keySort('ASC',  cmp );
16579             this.topModule.modules.each(addMod);
16580         } 
16581         return mods;
16582     },
16583     
16584      /**
16585      * Build the registered modules.
16586      * @param {Object} parent element.
16587      * @param {Function} optional method to call after module has been added.
16588      * 
16589      */ 
16590    
16591     build : function(opts) 
16592     {
16593         
16594         if (typeof(opts) != 'undefined') {
16595             Roo.apply(this,opts);
16596         }
16597         
16598         this.preBuild();
16599         var mods = this.buildOrder();
16600       
16601         //this.allmods = mods;
16602         //Roo.debug && Roo.log(mods);
16603         //return;
16604         if (!mods.length) { // should not happen
16605             throw "NO modules!!!";
16606         }
16607         
16608         
16609         var msg = "Building Interface...";
16610         // flash it up as modal - so we store the mask!?
16611         if (!this.hideProgress && Roo.MessageBox) {
16612             Roo.MessageBox.show({ title: 'loading' });
16613             Roo.MessageBox.show({
16614                title: "Please wait...",
16615                msg: msg,
16616                width:450,
16617                progress:true,
16618                buttons : false,
16619                closable:false,
16620                modal: false
16621               
16622             });
16623         }
16624         var total = mods.length;
16625         
16626         var _this = this;
16627         var progressRun = function() {
16628             if (!mods.length) {
16629                 Roo.debug && Roo.log('hide?');
16630                 if (!this.hideProgress && Roo.MessageBox) {
16631                     Roo.MessageBox.hide();
16632                 }
16633                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16634                 
16635                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16636                 
16637                 // THE END...
16638                 return false;   
16639             }
16640             
16641             var m = mods.shift();
16642             
16643             
16644             Roo.debug && Roo.log(m);
16645             // not sure if this is supported any more.. - modules that are are just function
16646             if (typeof(m) == 'function') { 
16647                 m.call(this);
16648                 return progressRun.defer(10, _this);
16649             } 
16650             
16651             
16652             msg = "Building Interface " + (total  - mods.length) + 
16653                     " of " + total + 
16654                     (m.name ? (' - ' + m.name) : '');
16655                         Roo.debug && Roo.log(msg);
16656             if (!_this.hideProgress &&  Roo.MessageBox) { 
16657                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16658             }
16659             
16660          
16661             // is the module disabled?
16662             var disabled = (typeof(m.disabled) == 'function') ?
16663                 m.disabled.call(m.module.disabled) : m.disabled;    
16664             
16665             
16666             if (disabled) {
16667                 return progressRun(); // we do not update the display!
16668             }
16669             
16670             // now build 
16671             
16672                         
16673                         
16674             m.render();
16675             // it's 10 on top level, and 1 on others??? why...
16676             return progressRun.defer(10, _this);
16677              
16678         }
16679         progressRun.defer(1, _this);
16680      
16681         
16682         
16683     },
16684     /**
16685      * Overlay a set of modified strings onto a component
16686      * This is dependant on our builder exporting the strings and 'named strings' elements.
16687      * 
16688      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16689      * @param {Object} associative array of 'named' string and it's new value.
16690      * 
16691      */
16692         overlayStrings : function( component, strings )
16693     {
16694         if (typeof(component['_named_strings']) == 'undefined') {
16695             throw "ERROR: component does not have _named_strings";
16696         }
16697         for ( var k in strings ) {
16698             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16699             if (md !== false) {
16700                 component['_strings'][md] = strings[k];
16701             } else {
16702                 Roo.log('could not find named string: ' + k + ' in');
16703                 Roo.log(component);
16704             }
16705             
16706         }
16707         
16708     },
16709     
16710         
16711         /**
16712          * Event Object.
16713          *
16714          *
16715          */
16716         event: false, 
16717     /**
16718          * wrapper for event.on - aliased later..  
16719          * Typically use to register a event handler for register:
16720          *
16721          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16722          *
16723          */
16724     on : false
16725    
16726     
16727     
16728 });
16729
16730 Roo.XComponent.event = new Roo.util.Observable({
16731                 events : { 
16732                         /**
16733                          * @event register
16734                          * Fires when an Component is registered,
16735                          * set the disable property on the Component to stop registration.
16736                          * @param {Roo.XComponent} c the component being registerd.
16737                          * 
16738                          */
16739                         'register' : true,
16740             /**
16741                          * @event beforebuild
16742                          * Fires before each Component is built
16743                          * can be used to apply permissions.
16744                          * @param {Roo.XComponent} c the component being registerd.
16745                          * 
16746                          */
16747                         'beforebuild' : true,
16748                         /**
16749                          * @event buildcomplete
16750                          * Fires on the top level element when all elements have been built
16751                          * @param {Roo.XComponent} the top level component.
16752                          */
16753                         'buildcomplete' : true
16754                         
16755                 }
16756 });
16757
16758 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16759  //
16760  /**
16761  * marked - a markdown parser
16762  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16763  * https://github.com/chjj/marked
16764  */
16765
16766
16767 /**
16768  *
16769  * Roo.Markdown - is a very crude wrapper around marked..
16770  *
16771  * usage:
16772  * 
16773  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16774  * 
16775  * Note: move the sample code to the bottom of this
16776  * file before uncommenting it.
16777  *
16778  */
16779
16780 Roo.Markdown = {};
16781 Roo.Markdown.toHtml = function(text) {
16782     
16783     var c = new Roo.Markdown.marked.setOptions({
16784             renderer: new Roo.Markdown.marked.Renderer(),
16785             gfm: true,
16786             tables: true,
16787             breaks: false,
16788             pedantic: false,
16789             sanitize: false,
16790             smartLists: true,
16791             smartypants: false
16792           });
16793     // A FEW HACKS!!?
16794     
16795     text = text.replace(/\\\n/g,' ');
16796     return Roo.Markdown.marked(text);
16797 };
16798 //
16799 // converter
16800 //
16801 // Wraps all "globals" so that the only thing
16802 // exposed is makeHtml().
16803 //
16804 (function() {
16805     
16806     /**
16807      * Block-Level Grammar
16808      */
16809     
16810     var block = {
16811       newline: /^\n+/,
16812       code: /^( {4}[^\n]+\n*)+/,
16813       fences: noop,
16814       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16815       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16816       nptable: noop,
16817       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16818       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16819       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16820       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16821       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16822       table: noop,
16823       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16824       text: /^[^\n]+/
16825     };
16826     
16827     block.bullet = /(?:[*+-]|\d+\.)/;
16828     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16829     block.item = replace(block.item, 'gm')
16830       (/bull/g, block.bullet)
16831       ();
16832     
16833     block.list = replace(block.list)
16834       (/bull/g, block.bullet)
16835       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16836       ('def', '\\n+(?=' + block.def.source + ')')
16837       ();
16838     
16839     block.blockquote = replace(block.blockquote)
16840       ('def', block.def)
16841       ();
16842     
16843     block._tag = '(?!(?:'
16844       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16845       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16846       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16847     
16848     block.html = replace(block.html)
16849       ('comment', /<!--[\s\S]*?-->/)
16850       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16851       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16852       (/tag/g, block._tag)
16853       ();
16854     
16855     block.paragraph = replace(block.paragraph)
16856       ('hr', block.hr)
16857       ('heading', block.heading)
16858       ('lheading', block.lheading)
16859       ('blockquote', block.blockquote)
16860       ('tag', '<' + block._tag)
16861       ('def', block.def)
16862       ();
16863     
16864     /**
16865      * Normal Block Grammar
16866      */
16867     
16868     block.normal = merge({}, block);
16869     
16870     /**
16871      * GFM Block Grammar
16872      */
16873     
16874     block.gfm = merge({}, block.normal, {
16875       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16876       paragraph: /^/,
16877       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16878     });
16879     
16880     block.gfm.paragraph = replace(block.paragraph)
16881       ('(?!', '(?!'
16882         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16883         + block.list.source.replace('\\1', '\\3') + '|')
16884       ();
16885     
16886     /**
16887      * GFM + Tables Block Grammar
16888      */
16889     
16890     block.tables = merge({}, block.gfm, {
16891       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16892       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16893     });
16894     
16895     /**
16896      * Block Lexer
16897      */
16898     
16899     function Lexer(options) {
16900       this.tokens = [];
16901       this.tokens.links = {};
16902       this.options = options || marked.defaults;
16903       this.rules = block.normal;
16904     
16905       if (this.options.gfm) {
16906         if (this.options.tables) {
16907           this.rules = block.tables;
16908         } else {
16909           this.rules = block.gfm;
16910         }
16911       }
16912     }
16913     
16914     /**
16915      * Expose Block Rules
16916      */
16917     
16918     Lexer.rules = block;
16919     
16920     /**
16921      * Static Lex Method
16922      */
16923     
16924     Lexer.lex = function(src, options) {
16925       var lexer = new Lexer(options);
16926       return lexer.lex(src);
16927     };
16928     
16929     /**
16930      * Preprocessing
16931      */
16932     
16933     Lexer.prototype.lex = function(src) {
16934       src = src
16935         .replace(/\r\n|\r/g, '\n')
16936         .replace(/\t/g, '    ')
16937         .replace(/\u00a0/g, ' ')
16938         .replace(/\u2424/g, '\n');
16939     
16940       return this.token(src, true);
16941     };
16942     
16943     /**
16944      * Lexing
16945      */
16946     
16947     Lexer.prototype.token = function(src, top, bq) {
16948       var src = src.replace(/^ +$/gm, '')
16949         , next
16950         , loose
16951         , cap
16952         , bull
16953         , b
16954         , item
16955         , space
16956         , i
16957         , l;
16958     
16959       while (src) {
16960         // newline
16961         if (cap = this.rules.newline.exec(src)) {
16962           src = src.substring(cap[0].length);
16963           if (cap[0].length > 1) {
16964             this.tokens.push({
16965               type: 'space'
16966             });
16967           }
16968         }
16969     
16970         // code
16971         if (cap = this.rules.code.exec(src)) {
16972           src = src.substring(cap[0].length);
16973           cap = cap[0].replace(/^ {4}/gm, '');
16974           this.tokens.push({
16975             type: 'code',
16976             text: !this.options.pedantic
16977               ? cap.replace(/\n+$/, '')
16978               : cap
16979           });
16980           continue;
16981         }
16982     
16983         // fences (gfm)
16984         if (cap = this.rules.fences.exec(src)) {
16985           src = src.substring(cap[0].length);
16986           this.tokens.push({
16987             type: 'code',
16988             lang: cap[2],
16989             text: cap[3] || ''
16990           });
16991           continue;
16992         }
16993     
16994         // heading
16995         if (cap = this.rules.heading.exec(src)) {
16996           src = src.substring(cap[0].length);
16997           this.tokens.push({
16998             type: 'heading',
16999             depth: cap[1].length,
17000             text: cap[2]
17001           });
17002           continue;
17003         }
17004     
17005         // table no leading pipe (gfm)
17006         if (top && (cap = this.rules.nptable.exec(src))) {
17007           src = src.substring(cap[0].length);
17008     
17009           item = {
17010             type: 'table',
17011             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17012             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17013             cells: cap[3].replace(/\n$/, '').split('\n')
17014           };
17015     
17016           for (i = 0; i < item.align.length; i++) {
17017             if (/^ *-+: *$/.test(item.align[i])) {
17018               item.align[i] = 'right';
17019             } else if (/^ *:-+: *$/.test(item.align[i])) {
17020               item.align[i] = 'center';
17021             } else if (/^ *:-+ *$/.test(item.align[i])) {
17022               item.align[i] = 'left';
17023             } else {
17024               item.align[i] = null;
17025             }
17026           }
17027     
17028           for (i = 0; i < item.cells.length; i++) {
17029             item.cells[i] = item.cells[i].split(/ *\| */);
17030           }
17031     
17032           this.tokens.push(item);
17033     
17034           continue;
17035         }
17036     
17037         // lheading
17038         if (cap = this.rules.lheading.exec(src)) {
17039           src = src.substring(cap[0].length);
17040           this.tokens.push({
17041             type: 'heading',
17042             depth: cap[2] === '=' ? 1 : 2,
17043             text: cap[1]
17044           });
17045           continue;
17046         }
17047     
17048         // hr
17049         if (cap = this.rules.hr.exec(src)) {
17050           src = src.substring(cap[0].length);
17051           this.tokens.push({
17052             type: 'hr'
17053           });
17054           continue;
17055         }
17056     
17057         // blockquote
17058         if (cap = this.rules.blockquote.exec(src)) {
17059           src = src.substring(cap[0].length);
17060     
17061           this.tokens.push({
17062             type: 'blockquote_start'
17063           });
17064     
17065           cap = cap[0].replace(/^ *> ?/gm, '');
17066     
17067           // Pass `top` to keep the current
17068           // "toplevel" state. This is exactly
17069           // how markdown.pl works.
17070           this.token(cap, top, true);
17071     
17072           this.tokens.push({
17073             type: 'blockquote_end'
17074           });
17075     
17076           continue;
17077         }
17078     
17079         // list
17080         if (cap = this.rules.list.exec(src)) {
17081           src = src.substring(cap[0].length);
17082           bull = cap[2];
17083     
17084           this.tokens.push({
17085             type: 'list_start',
17086             ordered: bull.length > 1
17087           });
17088     
17089           // Get each top-level item.
17090           cap = cap[0].match(this.rules.item);
17091     
17092           next = false;
17093           l = cap.length;
17094           i = 0;
17095     
17096           for (; i < l; i++) {
17097             item = cap[i];
17098     
17099             // Remove the list item's bullet
17100             // so it is seen as the next token.
17101             space = item.length;
17102             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17103     
17104             // Outdent whatever the
17105             // list item contains. Hacky.
17106             if (~item.indexOf('\n ')) {
17107               space -= item.length;
17108               item = !this.options.pedantic
17109                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17110                 : item.replace(/^ {1,4}/gm, '');
17111             }
17112     
17113             // Determine whether the next list item belongs here.
17114             // Backpedal if it does not belong in this list.
17115             if (this.options.smartLists && i !== l - 1) {
17116               b = block.bullet.exec(cap[i + 1])[0];
17117               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17118                 src = cap.slice(i + 1).join('\n') + src;
17119                 i = l - 1;
17120               }
17121             }
17122     
17123             // Determine whether item is loose or not.
17124             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17125             // for discount behavior.
17126             loose = next || /\n\n(?!\s*$)/.test(item);
17127             if (i !== l - 1) {
17128               next = item.charAt(item.length - 1) === '\n';
17129               if (!loose) { loose = next; }
17130             }
17131     
17132             this.tokens.push({
17133               type: loose
17134                 ? 'loose_item_start'
17135                 : 'list_item_start'
17136             });
17137     
17138             // Recurse.
17139             this.token(item, false, bq);
17140     
17141             this.tokens.push({
17142               type: 'list_item_end'
17143             });
17144           }
17145     
17146           this.tokens.push({
17147             type: 'list_end'
17148           });
17149     
17150           continue;
17151         }
17152     
17153         // html
17154         if (cap = this.rules.html.exec(src)) {
17155           src = src.substring(cap[0].length);
17156           this.tokens.push({
17157             type: this.options.sanitize
17158               ? 'paragraph'
17159               : 'html',
17160             pre: !this.options.sanitizer
17161               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17162             text: cap[0]
17163           });
17164           continue;
17165         }
17166     
17167         // def
17168         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17169           src = src.substring(cap[0].length);
17170           this.tokens.links[cap[1].toLowerCase()] = {
17171             href: cap[2],
17172             title: cap[3]
17173           };
17174           continue;
17175         }
17176     
17177         // table (gfm)
17178         if (top && (cap = this.rules.table.exec(src))) {
17179           src = src.substring(cap[0].length);
17180     
17181           item = {
17182             type: 'table',
17183             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17184             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17185             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17186           };
17187     
17188           for (i = 0; i < item.align.length; i++) {
17189             if (/^ *-+: *$/.test(item.align[i])) {
17190               item.align[i] = 'right';
17191             } else if (/^ *:-+: *$/.test(item.align[i])) {
17192               item.align[i] = 'center';
17193             } else if (/^ *:-+ *$/.test(item.align[i])) {
17194               item.align[i] = 'left';
17195             } else {
17196               item.align[i] = null;
17197             }
17198           }
17199     
17200           for (i = 0; i < item.cells.length; i++) {
17201             item.cells[i] = item.cells[i]
17202               .replace(/^ *\| *| *\| *$/g, '')
17203               .split(/ *\| */);
17204           }
17205     
17206           this.tokens.push(item);
17207     
17208           continue;
17209         }
17210     
17211         // top-level paragraph
17212         if (top && (cap = this.rules.paragraph.exec(src))) {
17213           src = src.substring(cap[0].length);
17214           this.tokens.push({
17215             type: 'paragraph',
17216             text: cap[1].charAt(cap[1].length - 1) === '\n'
17217               ? cap[1].slice(0, -1)
17218               : cap[1]
17219           });
17220           continue;
17221         }
17222     
17223         // text
17224         if (cap = this.rules.text.exec(src)) {
17225           // Top-level should never reach here.
17226           src = src.substring(cap[0].length);
17227           this.tokens.push({
17228             type: 'text',
17229             text: cap[0]
17230           });
17231           continue;
17232         }
17233     
17234         if (src) {
17235           throw new
17236             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17237         }
17238       }
17239     
17240       return this.tokens;
17241     };
17242     
17243     /**
17244      * Inline-Level Grammar
17245      */
17246     
17247     var inline = {
17248       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17249       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17250       url: noop,
17251       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17252       link: /^!?\[(inside)\]\(href\)/,
17253       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17254       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17255       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17256       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17257       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17258       br: /^ {2,}\n(?!\s*$)/,
17259       del: noop,
17260       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17261     };
17262     
17263     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17264     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17265     
17266     inline.link = replace(inline.link)
17267       ('inside', inline._inside)
17268       ('href', inline._href)
17269       ();
17270     
17271     inline.reflink = replace(inline.reflink)
17272       ('inside', inline._inside)
17273       ();
17274     
17275     /**
17276      * Normal Inline Grammar
17277      */
17278     
17279     inline.normal = merge({}, inline);
17280     
17281     /**
17282      * Pedantic Inline Grammar
17283      */
17284     
17285     inline.pedantic = merge({}, inline.normal, {
17286       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17287       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17288     });
17289     
17290     /**
17291      * GFM Inline Grammar
17292      */
17293     
17294     inline.gfm = merge({}, inline.normal, {
17295       escape: replace(inline.escape)('])', '~|])')(),
17296       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17297       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17298       text: replace(inline.text)
17299         (']|', '~]|')
17300         ('|', '|https?://|')
17301         ()
17302     });
17303     
17304     /**
17305      * GFM + Line Breaks Inline Grammar
17306      */
17307     
17308     inline.breaks = merge({}, inline.gfm, {
17309       br: replace(inline.br)('{2,}', '*')(),
17310       text: replace(inline.gfm.text)('{2,}', '*')()
17311     });
17312     
17313     /**
17314      * Inline Lexer & Compiler
17315      */
17316     
17317     function InlineLexer(links, options) {
17318       this.options = options || marked.defaults;
17319       this.links = links;
17320       this.rules = inline.normal;
17321       this.renderer = this.options.renderer || new Renderer;
17322       this.renderer.options = this.options;
17323     
17324       if (!this.links) {
17325         throw new
17326           Error('Tokens array requires a `links` property.');
17327       }
17328     
17329       if (this.options.gfm) {
17330         if (this.options.breaks) {
17331           this.rules = inline.breaks;
17332         } else {
17333           this.rules = inline.gfm;
17334         }
17335       } else if (this.options.pedantic) {
17336         this.rules = inline.pedantic;
17337       }
17338     }
17339     
17340     /**
17341      * Expose Inline Rules
17342      */
17343     
17344     InlineLexer.rules = inline;
17345     
17346     /**
17347      * Static Lexing/Compiling Method
17348      */
17349     
17350     InlineLexer.output = function(src, links, options) {
17351       var inline = new InlineLexer(links, options);
17352       return inline.output(src);
17353     };
17354     
17355     /**
17356      * Lexing/Compiling
17357      */
17358     
17359     InlineLexer.prototype.output = function(src) {
17360       var out = ''
17361         , link
17362         , text
17363         , href
17364         , cap;
17365     
17366       while (src) {
17367         // escape
17368         if (cap = this.rules.escape.exec(src)) {
17369           src = src.substring(cap[0].length);
17370           out += cap[1];
17371           continue;
17372         }
17373     
17374         // autolink
17375         if (cap = this.rules.autolink.exec(src)) {
17376           src = src.substring(cap[0].length);
17377           if (cap[2] === '@') {
17378             text = cap[1].charAt(6) === ':'
17379               ? this.mangle(cap[1].substring(7))
17380               : this.mangle(cap[1]);
17381             href = this.mangle('mailto:') + text;
17382           } else {
17383             text = escape(cap[1]);
17384             href = text;
17385           }
17386           out += this.renderer.link(href, null, text);
17387           continue;
17388         }
17389     
17390         // url (gfm)
17391         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17392           src = src.substring(cap[0].length);
17393           text = escape(cap[1]);
17394           href = text;
17395           out += this.renderer.link(href, null, text);
17396           continue;
17397         }
17398     
17399         // tag
17400         if (cap = this.rules.tag.exec(src)) {
17401           if (!this.inLink && /^<a /i.test(cap[0])) {
17402             this.inLink = true;
17403           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17404             this.inLink = false;
17405           }
17406           src = src.substring(cap[0].length);
17407           out += this.options.sanitize
17408             ? this.options.sanitizer
17409               ? this.options.sanitizer(cap[0])
17410               : escape(cap[0])
17411             : cap[0];
17412           continue;
17413         }
17414     
17415         // link
17416         if (cap = this.rules.link.exec(src)) {
17417           src = src.substring(cap[0].length);
17418           this.inLink = true;
17419           out += this.outputLink(cap, {
17420             href: cap[2],
17421             title: cap[3]
17422           });
17423           this.inLink = false;
17424           continue;
17425         }
17426     
17427         // reflink, nolink
17428         if ((cap = this.rules.reflink.exec(src))
17429             || (cap = this.rules.nolink.exec(src))) {
17430           src = src.substring(cap[0].length);
17431           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17432           link = this.links[link.toLowerCase()];
17433           if (!link || !link.href) {
17434             out += cap[0].charAt(0);
17435             src = cap[0].substring(1) + src;
17436             continue;
17437           }
17438           this.inLink = true;
17439           out += this.outputLink(cap, link);
17440           this.inLink = false;
17441           continue;
17442         }
17443     
17444         // strong
17445         if (cap = this.rules.strong.exec(src)) {
17446           src = src.substring(cap[0].length);
17447           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17448           continue;
17449         }
17450     
17451         // em
17452         if (cap = this.rules.em.exec(src)) {
17453           src = src.substring(cap[0].length);
17454           out += this.renderer.em(this.output(cap[2] || cap[1]));
17455           continue;
17456         }
17457     
17458         // code
17459         if (cap = this.rules.code.exec(src)) {
17460           src = src.substring(cap[0].length);
17461           out += this.renderer.codespan(escape(cap[2], true));
17462           continue;
17463         }
17464     
17465         // br
17466         if (cap = this.rules.br.exec(src)) {
17467           src = src.substring(cap[0].length);
17468           out += this.renderer.br();
17469           continue;
17470         }
17471     
17472         // del (gfm)
17473         if (cap = this.rules.del.exec(src)) {
17474           src = src.substring(cap[0].length);
17475           out += this.renderer.del(this.output(cap[1]));
17476           continue;
17477         }
17478     
17479         // text
17480         if (cap = this.rules.text.exec(src)) {
17481           src = src.substring(cap[0].length);
17482           out += this.renderer.text(escape(this.smartypants(cap[0])));
17483           continue;
17484         }
17485     
17486         if (src) {
17487           throw new
17488             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17489         }
17490       }
17491     
17492       return out;
17493     };
17494     
17495     /**
17496      * Compile Link
17497      */
17498     
17499     InlineLexer.prototype.outputLink = function(cap, link) {
17500       var href = escape(link.href)
17501         , title = link.title ? escape(link.title) : null;
17502     
17503       return cap[0].charAt(0) !== '!'
17504         ? this.renderer.link(href, title, this.output(cap[1]))
17505         : this.renderer.image(href, title, escape(cap[1]));
17506     };
17507     
17508     /**
17509      * Smartypants Transformations
17510      */
17511     
17512     InlineLexer.prototype.smartypants = function(text) {
17513       if (!this.options.smartypants)  { return text; }
17514       return text
17515         // em-dashes
17516         .replace(/---/g, '\u2014')
17517         // en-dashes
17518         .replace(/--/g, '\u2013')
17519         // opening singles
17520         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17521         // closing singles & apostrophes
17522         .replace(/'/g, '\u2019')
17523         // opening doubles
17524         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17525         // closing doubles
17526         .replace(/"/g, '\u201d')
17527         // ellipses
17528         .replace(/\.{3}/g, '\u2026');
17529     };
17530     
17531     /**
17532      * Mangle Links
17533      */
17534     
17535     InlineLexer.prototype.mangle = function(text) {
17536       if (!this.options.mangle) { return text; }
17537       var out = ''
17538         , l = text.length
17539         , i = 0
17540         , ch;
17541     
17542       for (; i < l; i++) {
17543         ch = text.charCodeAt(i);
17544         if (Math.random() > 0.5) {
17545           ch = 'x' + ch.toString(16);
17546         }
17547         out += '&#' + ch + ';';
17548       }
17549     
17550       return out;
17551     };
17552     
17553     /**
17554      * Renderer
17555      */
17556     
17557     function Renderer(options) {
17558       this.options = options || {};
17559     }
17560     
17561     Renderer.prototype.code = function(code, lang, escaped) {
17562       if (this.options.highlight) {
17563         var out = this.options.highlight(code, lang);
17564         if (out != null && out !== code) {
17565           escaped = true;
17566           code = out;
17567         }
17568       } else {
17569             // hack!!! - it's already escapeD?
17570             escaped = true;
17571       }
17572     
17573       if (!lang) {
17574         return '<pre><code>'
17575           + (escaped ? code : escape(code, true))
17576           + '\n</code></pre>';
17577       }
17578     
17579       return '<pre><code class="'
17580         + this.options.langPrefix
17581         + escape(lang, true)
17582         + '">'
17583         + (escaped ? code : escape(code, true))
17584         + '\n</code></pre>\n';
17585     };
17586     
17587     Renderer.prototype.blockquote = function(quote) {
17588       return '<blockquote>\n' + quote + '</blockquote>\n';
17589     };
17590     
17591     Renderer.prototype.html = function(html) {
17592       return html;
17593     };
17594     
17595     Renderer.prototype.heading = function(text, level, raw) {
17596       return '<h'
17597         + level
17598         + ' id="'
17599         + this.options.headerPrefix
17600         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17601         + '">'
17602         + text
17603         + '</h'
17604         + level
17605         + '>\n';
17606     };
17607     
17608     Renderer.prototype.hr = function() {
17609       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17610     };
17611     
17612     Renderer.prototype.list = function(body, ordered) {
17613       var type = ordered ? 'ol' : 'ul';
17614       return '<' + type + '>\n' + body + '</' + type + '>\n';
17615     };
17616     
17617     Renderer.prototype.listitem = function(text) {
17618       return '<li>' + text + '</li>\n';
17619     };
17620     
17621     Renderer.prototype.paragraph = function(text) {
17622       return '<p>' + text + '</p>\n';
17623     };
17624     
17625     Renderer.prototype.table = function(header, body) {
17626       return '<table class="table table-striped">\n'
17627         + '<thead>\n'
17628         + header
17629         + '</thead>\n'
17630         + '<tbody>\n'
17631         + body
17632         + '</tbody>\n'
17633         + '</table>\n';
17634     };
17635     
17636     Renderer.prototype.tablerow = function(content) {
17637       return '<tr>\n' + content + '</tr>\n';
17638     };
17639     
17640     Renderer.prototype.tablecell = function(content, flags) {
17641       var type = flags.header ? 'th' : 'td';
17642       var tag = flags.align
17643         ? '<' + type + ' style="text-align:' + flags.align + '">'
17644         : '<' + type + '>';
17645       return tag + content + '</' + type + '>\n';
17646     };
17647     
17648     // span level renderer
17649     Renderer.prototype.strong = function(text) {
17650       return '<strong>' + text + '</strong>';
17651     };
17652     
17653     Renderer.prototype.em = function(text) {
17654       return '<em>' + text + '</em>';
17655     };
17656     
17657     Renderer.prototype.codespan = function(text) {
17658       return '<code>' + text + '</code>';
17659     };
17660     
17661     Renderer.prototype.br = function() {
17662       return this.options.xhtml ? '<br/>' : '<br>';
17663     };
17664     
17665     Renderer.prototype.del = function(text) {
17666       return '<del>' + text + '</del>';
17667     };
17668     
17669     Renderer.prototype.link = function(href, title, text) {
17670       if (this.options.sanitize) {
17671         try {
17672           var prot = decodeURIComponent(unescape(href))
17673             .replace(/[^\w:]/g, '')
17674             .toLowerCase();
17675         } catch (e) {
17676           return '';
17677         }
17678         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17679           return '';
17680         }
17681       }
17682       var out = '<a href="' + href + '"';
17683       if (title) {
17684         out += ' title="' + title + '"';
17685       }
17686       out += '>' + text + '</a>';
17687       return out;
17688     };
17689     
17690     Renderer.prototype.image = function(href, title, text) {
17691       var out = '<img src="' + href + '" alt="' + text + '"';
17692       if (title) {
17693         out += ' title="' + title + '"';
17694       }
17695       out += this.options.xhtml ? '/>' : '>';
17696       return out;
17697     };
17698     
17699     Renderer.prototype.text = function(text) {
17700       return text;
17701     };
17702     
17703     /**
17704      * Parsing & Compiling
17705      */
17706     
17707     function Parser(options) {
17708       this.tokens = [];
17709       this.token = null;
17710       this.options = options || marked.defaults;
17711       this.options.renderer = this.options.renderer || new Renderer;
17712       this.renderer = this.options.renderer;
17713       this.renderer.options = this.options;
17714     }
17715     
17716     /**
17717      * Static Parse Method
17718      */
17719     
17720     Parser.parse = function(src, options, renderer) {
17721       var parser = new Parser(options, renderer);
17722       return parser.parse(src);
17723     };
17724     
17725     /**
17726      * Parse Loop
17727      */
17728     
17729     Parser.prototype.parse = function(src) {
17730       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17731       this.tokens = src.reverse();
17732     
17733       var out = '';
17734       while (this.next()) {
17735         out += this.tok();
17736       }
17737     
17738       return out;
17739     };
17740     
17741     /**
17742      * Next Token
17743      */
17744     
17745     Parser.prototype.next = function() {
17746       return this.token = this.tokens.pop();
17747     };
17748     
17749     /**
17750      * Preview Next Token
17751      */
17752     
17753     Parser.prototype.peek = function() {
17754       return this.tokens[this.tokens.length - 1] || 0;
17755     };
17756     
17757     /**
17758      * Parse Text Tokens
17759      */
17760     
17761     Parser.prototype.parseText = function() {
17762       var body = this.token.text;
17763     
17764       while (this.peek().type === 'text') {
17765         body += '\n' + this.next().text;
17766       }
17767     
17768       return this.inline.output(body);
17769     };
17770     
17771     /**
17772      * Parse Current Token
17773      */
17774     
17775     Parser.prototype.tok = function() {
17776       switch (this.token.type) {
17777         case 'space': {
17778           return '';
17779         }
17780         case 'hr': {
17781           return this.renderer.hr();
17782         }
17783         case 'heading': {
17784           return this.renderer.heading(
17785             this.inline.output(this.token.text),
17786             this.token.depth,
17787             this.token.text);
17788         }
17789         case 'code': {
17790           return this.renderer.code(this.token.text,
17791             this.token.lang,
17792             this.token.escaped);
17793         }
17794         case 'table': {
17795           var header = ''
17796             , body = ''
17797             , i
17798             , row
17799             , cell
17800             , flags
17801             , j;
17802     
17803           // header
17804           cell = '';
17805           for (i = 0; i < this.token.header.length; i++) {
17806             flags = { header: true, align: this.token.align[i] };
17807             cell += this.renderer.tablecell(
17808               this.inline.output(this.token.header[i]),
17809               { header: true, align: this.token.align[i] }
17810             );
17811           }
17812           header += this.renderer.tablerow(cell);
17813     
17814           for (i = 0; i < this.token.cells.length; i++) {
17815             row = this.token.cells[i];
17816     
17817             cell = '';
17818             for (j = 0; j < row.length; j++) {
17819               cell += this.renderer.tablecell(
17820                 this.inline.output(row[j]),
17821                 { header: false, align: this.token.align[j] }
17822               );
17823             }
17824     
17825             body += this.renderer.tablerow(cell);
17826           }
17827           return this.renderer.table(header, body);
17828         }
17829         case 'blockquote_start': {
17830           var body = '';
17831     
17832           while (this.next().type !== 'blockquote_end') {
17833             body += this.tok();
17834           }
17835     
17836           return this.renderer.blockquote(body);
17837         }
17838         case 'list_start': {
17839           var body = ''
17840             , ordered = this.token.ordered;
17841     
17842           while (this.next().type !== 'list_end') {
17843             body += this.tok();
17844           }
17845     
17846           return this.renderer.list(body, ordered);
17847         }
17848         case 'list_item_start': {
17849           var body = '';
17850     
17851           while (this.next().type !== 'list_item_end') {
17852             body += this.token.type === 'text'
17853               ? this.parseText()
17854               : this.tok();
17855           }
17856     
17857           return this.renderer.listitem(body);
17858         }
17859         case 'loose_item_start': {
17860           var body = '';
17861     
17862           while (this.next().type !== 'list_item_end') {
17863             body += this.tok();
17864           }
17865     
17866           return this.renderer.listitem(body);
17867         }
17868         case 'html': {
17869           var html = !this.token.pre && !this.options.pedantic
17870             ? this.inline.output(this.token.text)
17871             : this.token.text;
17872           return this.renderer.html(html);
17873         }
17874         case 'paragraph': {
17875           return this.renderer.paragraph(this.inline.output(this.token.text));
17876         }
17877         case 'text': {
17878           return this.renderer.paragraph(this.parseText());
17879         }
17880       }
17881     };
17882     
17883     /**
17884      * Helpers
17885      */
17886     
17887     function escape(html, encode) {
17888       return html
17889         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17890         .replace(/</g, '&lt;')
17891         .replace(/>/g, '&gt;')
17892         .replace(/"/g, '&quot;')
17893         .replace(/'/g, '&#39;');
17894     }
17895     
17896     function unescape(html) {
17897         // explicitly match decimal, hex, and named HTML entities 
17898       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17899         n = n.toLowerCase();
17900         if (n === 'colon') { return ':'; }
17901         if (n.charAt(0) === '#') {
17902           return n.charAt(1) === 'x'
17903             ? String.fromCharCode(parseInt(n.substring(2), 16))
17904             : String.fromCharCode(+n.substring(1));
17905         }
17906         return '';
17907       });
17908     }
17909     
17910     function replace(regex, opt) {
17911       regex = regex.source;
17912       opt = opt || '';
17913       return function self(name, val) {
17914         if (!name) { return new RegExp(regex, opt); }
17915         val = val.source || val;
17916         val = val.replace(/(^|[^\[])\^/g, '$1');
17917         regex = regex.replace(name, val);
17918         return self;
17919       };
17920     }
17921     
17922     function noop() {}
17923     noop.exec = noop;
17924     
17925     function merge(obj) {
17926       var i = 1
17927         , target
17928         , key;
17929     
17930       for (; i < arguments.length; i++) {
17931         target = arguments[i];
17932         for (key in target) {
17933           if (Object.prototype.hasOwnProperty.call(target, key)) {
17934             obj[key] = target[key];
17935           }
17936         }
17937       }
17938     
17939       return obj;
17940     }
17941     
17942     
17943     /**
17944      * Marked
17945      */
17946     
17947     function marked(src, opt, callback) {
17948       if (callback || typeof opt === 'function') {
17949         if (!callback) {
17950           callback = opt;
17951           opt = null;
17952         }
17953     
17954         opt = merge({}, marked.defaults, opt || {});
17955     
17956         var highlight = opt.highlight
17957           , tokens
17958           , pending
17959           , i = 0;
17960     
17961         try {
17962           tokens = Lexer.lex(src, opt)
17963         } catch (e) {
17964           return callback(e);
17965         }
17966     
17967         pending = tokens.length;
17968     
17969         var done = function(err) {
17970           if (err) {
17971             opt.highlight = highlight;
17972             return callback(err);
17973           }
17974     
17975           var out;
17976     
17977           try {
17978             out = Parser.parse(tokens, opt);
17979           } catch (e) {
17980             err = e;
17981           }
17982     
17983           opt.highlight = highlight;
17984     
17985           return err
17986             ? callback(err)
17987             : callback(null, out);
17988         };
17989     
17990         if (!highlight || highlight.length < 3) {
17991           return done();
17992         }
17993     
17994         delete opt.highlight;
17995     
17996         if (!pending) { return done(); }
17997     
17998         for (; i < tokens.length; i++) {
17999           (function(token) {
18000             if (token.type !== 'code') {
18001               return --pending || done();
18002             }
18003             return highlight(token.text, token.lang, function(err, code) {
18004               if (err) { return done(err); }
18005               if (code == null || code === token.text) {
18006                 return --pending || done();
18007               }
18008               token.text = code;
18009               token.escaped = true;
18010               --pending || done();
18011             });
18012           })(tokens[i]);
18013         }
18014     
18015         return;
18016       }
18017       try {
18018         if (opt) { opt = merge({}, marked.defaults, opt); }
18019         return Parser.parse(Lexer.lex(src, opt), opt);
18020       } catch (e) {
18021         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18022         if ((opt || marked.defaults).silent) {
18023           return '<p>An error occured:</p><pre>'
18024             + escape(e.message + '', true)
18025             + '</pre>';
18026         }
18027         throw e;
18028       }
18029     }
18030     
18031     /**
18032      * Options
18033      */
18034     
18035     marked.options =
18036     marked.setOptions = function(opt) {
18037       merge(marked.defaults, opt);
18038       return marked;
18039     };
18040     
18041     marked.defaults = {
18042       gfm: true,
18043       tables: true,
18044       breaks: false,
18045       pedantic: false,
18046       sanitize: false,
18047       sanitizer: null,
18048       mangle: true,
18049       smartLists: false,
18050       silent: false,
18051       highlight: null,
18052       langPrefix: 'lang-',
18053       smartypants: false,
18054       headerPrefix: '',
18055       renderer: new Renderer,
18056       xhtml: false
18057     };
18058     
18059     /**
18060      * Expose
18061      */
18062     
18063     marked.Parser = Parser;
18064     marked.parser = Parser.parse;
18065     
18066     marked.Renderer = Renderer;
18067     
18068     marked.Lexer = Lexer;
18069     marked.lexer = Lexer.lex;
18070     
18071     marked.InlineLexer = InlineLexer;
18072     marked.inlineLexer = InlineLexer.output;
18073     
18074     marked.parse = marked;
18075     
18076     Roo.Markdown.marked = marked;
18077
18078 })();/*
18079  * Based on:
18080  * Ext JS Library 1.1.1
18081  * Copyright(c) 2006-2007, Ext JS, LLC.
18082  *
18083  * Originally Released Under LGPL - original licence link has changed is not relivant.
18084  *
18085  * Fork - LGPL
18086  * <script type="text/javascript">
18087  */
18088
18089
18090
18091 /*
18092  * These classes are derivatives of the similarly named classes in the YUI Library.
18093  * The original license:
18094  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18095  * Code licensed under the BSD License:
18096  * http://developer.yahoo.net/yui/license.txt
18097  */
18098
18099 (function() {
18100
18101 var Event=Roo.EventManager;
18102 var Dom=Roo.lib.Dom;
18103
18104 /**
18105  * @class Roo.dd.DragDrop
18106  * @extends Roo.util.Observable
18107  * Defines the interface and base operation of items that that can be
18108  * dragged or can be drop targets.  It was designed to be extended, overriding
18109  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18110  * Up to three html elements can be associated with a DragDrop instance:
18111  * <ul>
18112  * <li>linked element: the element that is passed into the constructor.
18113  * This is the element which defines the boundaries for interaction with
18114  * other DragDrop objects.</li>
18115  * <li>handle element(s): The drag operation only occurs if the element that
18116  * was clicked matches a handle element.  By default this is the linked
18117  * element, but there are times that you will want only a portion of the
18118  * linked element to initiate the drag operation, and the setHandleElId()
18119  * method provides a way to define this.</li>
18120  * <li>drag element: this represents the element that would be moved along
18121  * with the cursor during a drag operation.  By default, this is the linked
18122  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18123  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18124  * </li>
18125  * </ul>
18126  * This class should not be instantiated until the onload event to ensure that
18127  * the associated elements are available.
18128  * The following would define a DragDrop obj that would interact with any
18129  * other DragDrop obj in the "group1" group:
18130  * <pre>
18131  *  dd = new Roo.dd.DragDrop("div1", "group1");
18132  * </pre>
18133  * Since none of the event handlers have been implemented, nothing would
18134  * actually happen if you were to run the code above.  Normally you would
18135  * override this class or one of the default implementations, but you can
18136  * also override the methods you want on an instance of the class...
18137  * <pre>
18138  *  dd.onDragDrop = function(e, id) {
18139  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18140  *  }
18141  * </pre>
18142  * @constructor
18143  * @param {String} id of the element that is linked to this instance
18144  * @param {String} sGroup the group of related DragDrop objects
18145  * @param {object} config an object containing configurable attributes
18146  *                Valid properties for DragDrop:
18147  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18148  */
18149 Roo.dd.DragDrop = function(id, sGroup, config) {
18150     if (id) {
18151         this.init(id, sGroup, config);
18152     }
18153     
18154 };
18155
18156 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18157
18158     /**
18159      * The id of the element associated with this object.  This is what we
18160      * refer to as the "linked element" because the size and position of
18161      * this element is used to determine when the drag and drop objects have
18162      * interacted.
18163      * @property id
18164      * @type String
18165      */
18166     id: null,
18167
18168     /**
18169      * Configuration attributes passed into the constructor
18170      * @property config
18171      * @type object
18172      */
18173     config: null,
18174
18175     /**
18176      * The id of the element that will be dragged.  By default this is same
18177      * as the linked element , but could be changed to another element. Ex:
18178      * Roo.dd.DDProxy
18179      * @property dragElId
18180      * @type String
18181      * @private
18182      */
18183     dragElId: null,
18184
18185     /**
18186      * the id of the element that initiates the drag operation.  By default
18187      * this is the linked element, but could be changed to be a child of this
18188      * element.  This lets us do things like only starting the drag when the
18189      * header element within the linked html element is clicked.
18190      * @property handleElId
18191      * @type String
18192      * @private
18193      */
18194     handleElId: null,
18195
18196     /**
18197      * An associative array of HTML tags that will be ignored if clicked.
18198      * @property invalidHandleTypes
18199      * @type {string: string}
18200      */
18201     invalidHandleTypes: null,
18202
18203     /**
18204      * An associative array of ids for elements that will be ignored if clicked
18205      * @property invalidHandleIds
18206      * @type {string: string}
18207      */
18208     invalidHandleIds: null,
18209
18210     /**
18211      * An indexted array of css class names for elements that will be ignored
18212      * if clicked.
18213      * @property invalidHandleClasses
18214      * @type string[]
18215      */
18216     invalidHandleClasses: null,
18217
18218     /**
18219      * The linked element's absolute X position at the time the drag was
18220      * started
18221      * @property startPageX
18222      * @type int
18223      * @private
18224      */
18225     startPageX: 0,
18226
18227     /**
18228      * The linked element's absolute X position at the time the drag was
18229      * started
18230      * @property startPageY
18231      * @type int
18232      * @private
18233      */
18234     startPageY: 0,
18235
18236     /**
18237      * The group defines a logical collection of DragDrop objects that are
18238      * related.  Instances only get events when interacting with other
18239      * DragDrop object in the same group.  This lets us define multiple
18240      * groups using a single DragDrop subclass if we want.
18241      * @property groups
18242      * @type {string: string}
18243      */
18244     groups: null,
18245
18246     /**
18247      * Individual drag/drop instances can be locked.  This will prevent
18248      * onmousedown start drag.
18249      * @property locked
18250      * @type boolean
18251      * @private
18252      */
18253     locked: false,
18254
18255     /**
18256      * Lock this instance
18257      * @method lock
18258      */
18259     lock: function() { this.locked = true; },
18260
18261     /**
18262      * Unlock this instace
18263      * @method unlock
18264      */
18265     unlock: function() { this.locked = false; },
18266
18267     /**
18268      * By default, all insances can be a drop target.  This can be disabled by
18269      * setting isTarget to false.
18270      * @method isTarget
18271      * @type boolean
18272      */
18273     isTarget: true,
18274
18275     /**
18276      * The padding configured for this drag and drop object for calculating
18277      * the drop zone intersection with this object.
18278      * @method padding
18279      * @type int[]
18280      */
18281     padding: null,
18282
18283     /**
18284      * Cached reference to the linked element
18285      * @property _domRef
18286      * @private
18287      */
18288     _domRef: null,
18289
18290     /**
18291      * Internal typeof flag
18292      * @property __ygDragDrop
18293      * @private
18294      */
18295     __ygDragDrop: true,
18296
18297     /**
18298      * Set to true when horizontal contraints are applied
18299      * @property constrainX
18300      * @type boolean
18301      * @private
18302      */
18303     constrainX: false,
18304
18305     /**
18306      * Set to true when vertical contraints are applied
18307      * @property constrainY
18308      * @type boolean
18309      * @private
18310      */
18311     constrainY: false,
18312
18313     /**
18314      * The left constraint
18315      * @property minX
18316      * @type int
18317      * @private
18318      */
18319     minX: 0,
18320
18321     /**
18322      * The right constraint
18323      * @property maxX
18324      * @type int
18325      * @private
18326      */
18327     maxX: 0,
18328
18329     /**
18330      * The up constraint
18331      * @property minY
18332      * @type int
18333      * @type int
18334      * @private
18335      */
18336     minY: 0,
18337
18338     /**
18339      * The down constraint
18340      * @property maxY
18341      * @type int
18342      * @private
18343      */
18344     maxY: 0,
18345
18346     /**
18347      * Maintain offsets when we resetconstraints.  Set to true when you want
18348      * the position of the element relative to its parent to stay the same
18349      * when the page changes
18350      *
18351      * @property maintainOffset
18352      * @type boolean
18353      */
18354     maintainOffset: false,
18355
18356     /**
18357      * Array of pixel locations the element will snap to if we specified a
18358      * horizontal graduation/interval.  This array is generated automatically
18359      * when you define a tick interval.
18360      * @property xTicks
18361      * @type int[]
18362      */
18363     xTicks: null,
18364
18365     /**
18366      * Array of pixel locations the element will snap to if we specified a
18367      * vertical graduation/interval.  This array is generated automatically
18368      * when you define a tick interval.
18369      * @property yTicks
18370      * @type int[]
18371      */
18372     yTicks: null,
18373
18374     /**
18375      * By default the drag and drop instance will only respond to the primary
18376      * button click (left button for a right-handed mouse).  Set to true to
18377      * allow drag and drop to start with any mouse click that is propogated
18378      * by the browser
18379      * @property primaryButtonOnly
18380      * @type boolean
18381      */
18382     primaryButtonOnly: true,
18383
18384     /**
18385      * The availabe property is false until the linked dom element is accessible.
18386      * @property available
18387      * @type boolean
18388      */
18389     available: false,
18390
18391     /**
18392      * By default, drags can only be initiated if the mousedown occurs in the
18393      * region the linked element is.  This is done in part to work around a
18394      * bug in some browsers that mis-report the mousedown if the previous
18395      * mouseup happened outside of the window.  This property is set to true
18396      * if outer handles are defined.
18397      *
18398      * @property hasOuterHandles
18399      * @type boolean
18400      * @default false
18401      */
18402     hasOuterHandles: false,
18403
18404     /**
18405      * Code that executes immediately before the startDrag event
18406      * @method b4StartDrag
18407      * @private
18408      */
18409     b4StartDrag: function(x, y) { },
18410
18411     /**
18412      * Abstract method called after a drag/drop object is clicked
18413      * and the drag or mousedown time thresholds have beeen met.
18414      * @method startDrag
18415      * @param {int} X click location
18416      * @param {int} Y click location
18417      */
18418     startDrag: function(x, y) { /* override this */ },
18419
18420     /**
18421      * Code that executes immediately before the onDrag event
18422      * @method b4Drag
18423      * @private
18424      */
18425     b4Drag: function(e) { },
18426
18427     /**
18428      * Abstract method called during the onMouseMove event while dragging an
18429      * object.
18430      * @method onDrag
18431      * @param {Event} e the mousemove event
18432      */
18433     onDrag: function(e) { /* override this */ },
18434
18435     /**
18436      * Abstract method called when this element fist begins hovering over
18437      * another DragDrop obj
18438      * @method onDragEnter
18439      * @param {Event} e the mousemove event
18440      * @param {String|DragDrop[]} id In POINT mode, the element
18441      * id this is hovering over.  In INTERSECT mode, an array of one or more
18442      * dragdrop items being hovered over.
18443      */
18444     onDragEnter: function(e, id) { /* override this */ },
18445
18446     /**
18447      * Code that executes immediately before the onDragOver event
18448      * @method b4DragOver
18449      * @private
18450      */
18451     b4DragOver: function(e) { },
18452
18453     /**
18454      * Abstract method called when this element is hovering over another
18455      * DragDrop obj
18456      * @method onDragOver
18457      * @param {Event} e the mousemove event
18458      * @param {String|DragDrop[]} id In POINT mode, the element
18459      * id this is hovering over.  In INTERSECT mode, an array of dd items
18460      * being hovered over.
18461      */
18462     onDragOver: function(e, id) { /* override this */ },
18463
18464     /**
18465      * Code that executes immediately before the onDragOut event
18466      * @method b4DragOut
18467      * @private
18468      */
18469     b4DragOut: function(e) { },
18470
18471     /**
18472      * Abstract method called when we are no longer hovering over an element
18473      * @method onDragOut
18474      * @param {Event} e the mousemove event
18475      * @param {String|DragDrop[]} id In POINT mode, the element
18476      * id this was hovering over.  In INTERSECT mode, an array of dd items
18477      * that the mouse is no longer over.
18478      */
18479     onDragOut: function(e, id) { /* override this */ },
18480
18481     /**
18482      * Code that executes immediately before the onDragDrop event
18483      * @method b4DragDrop
18484      * @private
18485      */
18486     b4DragDrop: function(e) { },
18487
18488     /**
18489      * Abstract method called when this item is dropped on another DragDrop
18490      * obj
18491      * @method onDragDrop
18492      * @param {Event} e the mouseup event
18493      * @param {String|DragDrop[]} id In POINT mode, the element
18494      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18495      * was dropped on.
18496      */
18497     onDragDrop: function(e, id) { /* override this */ },
18498
18499     /**
18500      * Abstract method called when this item is dropped on an area with no
18501      * drop target
18502      * @method onInvalidDrop
18503      * @param {Event} e the mouseup event
18504      */
18505     onInvalidDrop: function(e) { /* override this */ },
18506
18507     /**
18508      * Code that executes immediately before the endDrag event
18509      * @method b4EndDrag
18510      * @private
18511      */
18512     b4EndDrag: function(e) { },
18513
18514     /**
18515      * Fired when we are done dragging the object
18516      * @method endDrag
18517      * @param {Event} e the mouseup event
18518      */
18519     endDrag: function(e) { /* override this */ },
18520
18521     /**
18522      * Code executed immediately before the onMouseDown event
18523      * @method b4MouseDown
18524      * @param {Event} e the mousedown event
18525      * @private
18526      */
18527     b4MouseDown: function(e) {  },
18528
18529     /**
18530      * Event handler that fires when a drag/drop obj gets a mousedown
18531      * @method onMouseDown
18532      * @param {Event} e the mousedown event
18533      */
18534     onMouseDown: function(e) { /* override this */ },
18535
18536     /**
18537      * Event handler that fires when a drag/drop obj gets a mouseup
18538      * @method onMouseUp
18539      * @param {Event} e the mouseup event
18540      */
18541     onMouseUp: function(e) { /* override this */ },
18542
18543     /**
18544      * Override the onAvailable method to do what is needed after the initial
18545      * position was determined.
18546      * @method onAvailable
18547      */
18548     onAvailable: function () {
18549     },
18550
18551     /*
18552      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18553      * @type Object
18554      */
18555     defaultPadding : {left:0, right:0, top:0, bottom:0},
18556
18557     /*
18558      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18559  *
18560  * Usage:
18561  <pre><code>
18562  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18563                 { dragElId: "existingProxyDiv" });
18564  dd.startDrag = function(){
18565      this.constrainTo("parent-id");
18566  };
18567  </code></pre>
18568  * Or you can initalize it using the {@link Roo.Element} object:
18569  <pre><code>
18570  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18571      startDrag : function(){
18572          this.constrainTo("parent-id");
18573      }
18574  });
18575  </code></pre>
18576      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18577      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18578      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18579      * an object containing the sides to pad. For example: {right:10, bottom:10}
18580      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18581      */
18582     constrainTo : function(constrainTo, pad, inContent){
18583         if(typeof pad == "number"){
18584             pad = {left: pad, right:pad, top:pad, bottom:pad};
18585         }
18586         pad = pad || this.defaultPadding;
18587         var b = Roo.get(this.getEl()).getBox();
18588         var ce = Roo.get(constrainTo);
18589         var s = ce.getScroll();
18590         var c, cd = ce.dom;
18591         if(cd == document.body){
18592             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18593         }else{
18594             xy = ce.getXY();
18595             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18596         }
18597
18598
18599         var topSpace = b.y - c.y;
18600         var leftSpace = b.x - c.x;
18601
18602         this.resetConstraints();
18603         this.setXConstraint(leftSpace - (pad.left||0), // left
18604                 c.width - leftSpace - b.width - (pad.right||0) //right
18605         );
18606         this.setYConstraint(topSpace - (pad.top||0), //top
18607                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18608         );
18609     },
18610
18611     /**
18612      * Returns a reference to the linked element
18613      * @method getEl
18614      * @return {HTMLElement} the html element
18615      */
18616     getEl: function() {
18617         if (!this._domRef) {
18618             this._domRef = Roo.getDom(this.id);
18619         }
18620
18621         return this._domRef;
18622     },
18623
18624     /**
18625      * Returns a reference to the actual element to drag.  By default this is
18626      * the same as the html element, but it can be assigned to another
18627      * element. An example of this can be found in Roo.dd.DDProxy
18628      * @method getDragEl
18629      * @return {HTMLElement} the html element
18630      */
18631     getDragEl: function() {
18632         return Roo.getDom(this.dragElId);
18633     },
18634
18635     /**
18636      * Sets up the DragDrop object.  Must be called in the constructor of any
18637      * Roo.dd.DragDrop subclass
18638      * @method init
18639      * @param id the id of the linked element
18640      * @param {String} sGroup the group of related items
18641      * @param {object} config configuration attributes
18642      */
18643     init: function(id, sGroup, config) {
18644         this.initTarget(id, sGroup, config);
18645         if (!Roo.isTouch) {
18646             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18647         }
18648         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18649         // Event.on(this.id, "selectstart", Event.preventDefault);
18650     },
18651
18652     /**
18653      * Initializes Targeting functionality only... the object does not
18654      * get a mousedown handler.
18655      * @method initTarget
18656      * @param id the id of the linked element
18657      * @param {String} sGroup the group of related items
18658      * @param {object} config configuration attributes
18659      */
18660     initTarget: function(id, sGroup, config) {
18661
18662         // configuration attributes
18663         this.config = config || {};
18664
18665         // create a local reference to the drag and drop manager
18666         this.DDM = Roo.dd.DDM;
18667         // initialize the groups array
18668         this.groups = {};
18669
18670         // assume that we have an element reference instead of an id if the
18671         // parameter is not a string
18672         if (typeof id !== "string") {
18673             id = Roo.id(id);
18674         }
18675
18676         // set the id
18677         this.id = id;
18678
18679         // add to an interaction group
18680         this.addToGroup((sGroup) ? sGroup : "default");
18681
18682         // We don't want to register this as the handle with the manager
18683         // so we just set the id rather than calling the setter.
18684         this.handleElId = id;
18685
18686         // the linked element is the element that gets dragged by default
18687         this.setDragElId(id);
18688
18689         // by default, clicked anchors will not start drag operations.
18690         this.invalidHandleTypes = { A: "A" };
18691         this.invalidHandleIds = {};
18692         this.invalidHandleClasses = [];
18693
18694         this.applyConfig();
18695
18696         this.handleOnAvailable();
18697     },
18698
18699     /**
18700      * Applies the configuration parameters that were passed into the constructor.
18701      * This is supposed to happen at each level through the inheritance chain.  So
18702      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18703      * DragDrop in order to get all of the parameters that are available in
18704      * each object.
18705      * @method applyConfig
18706      */
18707     applyConfig: function() {
18708
18709         // configurable properties:
18710         //    padding, isTarget, maintainOffset, primaryButtonOnly
18711         this.padding           = this.config.padding || [0, 0, 0, 0];
18712         this.isTarget          = (this.config.isTarget !== false);
18713         this.maintainOffset    = (this.config.maintainOffset);
18714         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18715
18716     },
18717
18718     /**
18719      * Executed when the linked element is available
18720      * @method handleOnAvailable
18721      * @private
18722      */
18723     handleOnAvailable: function() {
18724         this.available = true;
18725         this.resetConstraints();
18726         this.onAvailable();
18727     },
18728
18729      /**
18730      * Configures the padding for the target zone in px.  Effectively expands
18731      * (or reduces) the virtual object size for targeting calculations.
18732      * Supports css-style shorthand; if only one parameter is passed, all sides
18733      * will have that padding, and if only two are passed, the top and bottom
18734      * will have the first param, the left and right the second.
18735      * @method setPadding
18736      * @param {int} iTop    Top pad
18737      * @param {int} iRight  Right pad
18738      * @param {int} iBot    Bot pad
18739      * @param {int} iLeft   Left pad
18740      */
18741     setPadding: function(iTop, iRight, iBot, iLeft) {
18742         // this.padding = [iLeft, iRight, iTop, iBot];
18743         if (!iRight && 0 !== iRight) {
18744             this.padding = [iTop, iTop, iTop, iTop];
18745         } else if (!iBot && 0 !== iBot) {
18746             this.padding = [iTop, iRight, iTop, iRight];
18747         } else {
18748             this.padding = [iTop, iRight, iBot, iLeft];
18749         }
18750     },
18751
18752     /**
18753      * Stores the initial placement of the linked element.
18754      * @method setInitialPosition
18755      * @param {int} diffX   the X offset, default 0
18756      * @param {int} diffY   the Y offset, default 0
18757      */
18758     setInitPosition: function(diffX, diffY) {
18759         var el = this.getEl();
18760
18761         if (!this.DDM.verifyEl(el)) {
18762             return;
18763         }
18764
18765         var dx = diffX || 0;
18766         var dy = diffY || 0;
18767
18768         var p = Dom.getXY( el );
18769
18770         this.initPageX = p[0] - dx;
18771         this.initPageY = p[1] - dy;
18772
18773         this.lastPageX = p[0];
18774         this.lastPageY = p[1];
18775
18776
18777         this.setStartPosition(p);
18778     },
18779
18780     /**
18781      * Sets the start position of the element.  This is set when the obj
18782      * is initialized, the reset when a drag is started.
18783      * @method setStartPosition
18784      * @param pos current position (from previous lookup)
18785      * @private
18786      */
18787     setStartPosition: function(pos) {
18788         var p = pos || Dom.getXY( this.getEl() );
18789         this.deltaSetXY = null;
18790
18791         this.startPageX = p[0];
18792         this.startPageY = p[1];
18793     },
18794
18795     /**
18796      * Add this instance to a group of related drag/drop objects.  All
18797      * instances belong to at least one group, and can belong to as many
18798      * groups as needed.
18799      * @method addToGroup
18800      * @param sGroup {string} the name of the group
18801      */
18802     addToGroup: function(sGroup) {
18803         this.groups[sGroup] = true;
18804         this.DDM.regDragDrop(this, sGroup);
18805     },
18806
18807     /**
18808      * Remove's this instance from the supplied interaction group
18809      * @method removeFromGroup
18810      * @param {string}  sGroup  The group to drop
18811      */
18812     removeFromGroup: function(sGroup) {
18813         if (this.groups[sGroup]) {
18814             delete this.groups[sGroup];
18815         }
18816
18817         this.DDM.removeDDFromGroup(this, sGroup);
18818     },
18819
18820     /**
18821      * Allows you to specify that an element other than the linked element
18822      * will be moved with the cursor during a drag
18823      * @method setDragElId
18824      * @param id {string} the id of the element that will be used to initiate the drag
18825      */
18826     setDragElId: function(id) {
18827         this.dragElId = id;
18828     },
18829
18830     /**
18831      * Allows you to specify a child of the linked element that should be
18832      * used to initiate the drag operation.  An example of this would be if
18833      * you have a content div with text and links.  Clicking anywhere in the
18834      * content area would normally start the drag operation.  Use this method
18835      * to specify that an element inside of the content div is the element
18836      * that starts the drag operation.
18837      * @method setHandleElId
18838      * @param id {string} the id of the element that will be used to
18839      * initiate the drag.
18840      */
18841     setHandleElId: function(id) {
18842         if (typeof id !== "string") {
18843             id = Roo.id(id);
18844         }
18845         this.handleElId = id;
18846         this.DDM.regHandle(this.id, id);
18847     },
18848
18849     /**
18850      * Allows you to set an element outside of the linked element as a drag
18851      * handle
18852      * @method setOuterHandleElId
18853      * @param id the id of the element that will be used to initiate the drag
18854      */
18855     setOuterHandleElId: function(id) {
18856         if (typeof id !== "string") {
18857             id = Roo.id(id);
18858         }
18859         Event.on(id, "mousedown",
18860                 this.handleMouseDown, this);
18861         this.setHandleElId(id);
18862
18863         this.hasOuterHandles = true;
18864     },
18865
18866     /**
18867      * Remove all drag and drop hooks for this element
18868      * @method unreg
18869      */
18870     unreg: function() {
18871         Event.un(this.id, "mousedown",
18872                 this.handleMouseDown);
18873         Event.un(this.id, "touchstart",
18874                 this.handleMouseDown);
18875         this._domRef = null;
18876         this.DDM._remove(this);
18877     },
18878
18879     destroy : function(){
18880         this.unreg();
18881     },
18882
18883     /**
18884      * Returns true if this instance is locked, or the drag drop mgr is locked
18885      * (meaning that all drag/drop is disabled on the page.)
18886      * @method isLocked
18887      * @return {boolean} true if this obj or all drag/drop is locked, else
18888      * false
18889      */
18890     isLocked: function() {
18891         return (this.DDM.isLocked() || this.locked);
18892     },
18893
18894     /**
18895      * Fired when this object is clicked
18896      * @method handleMouseDown
18897      * @param {Event} e
18898      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18899      * @private
18900      */
18901     handleMouseDown: function(e, oDD){
18902      
18903         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18904             //Roo.log('not touch/ button !=0');
18905             return;
18906         }
18907         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18908             return; // double touch..
18909         }
18910         
18911
18912         if (this.isLocked()) {
18913             //Roo.log('locked');
18914             return;
18915         }
18916
18917         this.DDM.refreshCache(this.groups);
18918 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18919         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18920         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18921             //Roo.log('no outer handes or not over target');
18922                 // do nothing.
18923         } else {
18924 //            Roo.log('check validator');
18925             if (this.clickValidator(e)) {
18926 //                Roo.log('validate success');
18927                 // set the initial element position
18928                 this.setStartPosition();
18929
18930
18931                 this.b4MouseDown(e);
18932                 this.onMouseDown(e);
18933
18934                 this.DDM.handleMouseDown(e, this);
18935
18936                 this.DDM.stopEvent(e);
18937             } else {
18938
18939
18940             }
18941         }
18942     },
18943
18944     clickValidator: function(e) {
18945         var target = e.getTarget();
18946         return ( this.isValidHandleChild(target) &&
18947                     (this.id == this.handleElId ||
18948                         this.DDM.handleWasClicked(target, this.id)) );
18949     },
18950
18951     /**
18952      * Allows you to specify a tag name that should not start a drag operation
18953      * when clicked.  This is designed to facilitate embedding links within a
18954      * drag handle that do something other than start the drag.
18955      * @method addInvalidHandleType
18956      * @param {string} tagName the type of element to exclude
18957      */
18958     addInvalidHandleType: function(tagName) {
18959         var type = tagName.toUpperCase();
18960         this.invalidHandleTypes[type] = type;
18961     },
18962
18963     /**
18964      * Lets you to specify an element id for a child of a drag handle
18965      * that should not initiate a drag
18966      * @method addInvalidHandleId
18967      * @param {string} id the element id of the element you wish to ignore
18968      */
18969     addInvalidHandleId: function(id) {
18970         if (typeof id !== "string") {
18971             id = Roo.id(id);
18972         }
18973         this.invalidHandleIds[id] = id;
18974     },
18975
18976     /**
18977      * Lets you specify a css class of elements that will not initiate a drag
18978      * @method addInvalidHandleClass
18979      * @param {string} cssClass the class of the elements you wish to ignore
18980      */
18981     addInvalidHandleClass: function(cssClass) {
18982         this.invalidHandleClasses.push(cssClass);
18983     },
18984
18985     /**
18986      * Unsets an excluded tag name set by addInvalidHandleType
18987      * @method removeInvalidHandleType
18988      * @param {string} tagName the type of element to unexclude
18989      */
18990     removeInvalidHandleType: function(tagName) {
18991         var type = tagName.toUpperCase();
18992         // this.invalidHandleTypes[type] = null;
18993         delete this.invalidHandleTypes[type];
18994     },
18995
18996     /**
18997      * Unsets an invalid handle id
18998      * @method removeInvalidHandleId
18999      * @param {string} id the id of the element to re-enable
19000      */
19001     removeInvalidHandleId: function(id) {
19002         if (typeof id !== "string") {
19003             id = Roo.id(id);
19004         }
19005         delete this.invalidHandleIds[id];
19006     },
19007
19008     /**
19009      * Unsets an invalid css class
19010      * @method removeInvalidHandleClass
19011      * @param {string} cssClass the class of the element(s) you wish to
19012      * re-enable
19013      */
19014     removeInvalidHandleClass: function(cssClass) {
19015         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19016             if (this.invalidHandleClasses[i] == cssClass) {
19017                 delete this.invalidHandleClasses[i];
19018             }
19019         }
19020     },
19021
19022     /**
19023      * Checks the tag exclusion list to see if this click should be ignored
19024      * @method isValidHandleChild
19025      * @param {HTMLElement} node the HTMLElement to evaluate
19026      * @return {boolean} true if this is a valid tag type, false if not
19027      */
19028     isValidHandleChild: function(node) {
19029
19030         var valid = true;
19031         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19032         var nodeName;
19033         try {
19034             nodeName = node.nodeName.toUpperCase();
19035         } catch(e) {
19036             nodeName = node.nodeName;
19037         }
19038         valid = valid && !this.invalidHandleTypes[nodeName];
19039         valid = valid && !this.invalidHandleIds[node.id];
19040
19041         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19042             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19043         }
19044
19045
19046         return valid;
19047
19048     },
19049
19050     /**
19051      * Create the array of horizontal tick marks if an interval was specified
19052      * in setXConstraint().
19053      * @method setXTicks
19054      * @private
19055      */
19056     setXTicks: function(iStartX, iTickSize) {
19057         this.xTicks = [];
19058         this.xTickSize = iTickSize;
19059
19060         var tickMap = {};
19061
19062         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19063             if (!tickMap[i]) {
19064                 this.xTicks[this.xTicks.length] = i;
19065                 tickMap[i] = true;
19066             }
19067         }
19068
19069         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19070             if (!tickMap[i]) {
19071                 this.xTicks[this.xTicks.length] = i;
19072                 tickMap[i] = true;
19073             }
19074         }
19075
19076         this.xTicks.sort(this.DDM.numericSort) ;
19077     },
19078
19079     /**
19080      * Create the array of vertical tick marks if an interval was specified in
19081      * setYConstraint().
19082      * @method setYTicks
19083      * @private
19084      */
19085     setYTicks: function(iStartY, iTickSize) {
19086         this.yTicks = [];
19087         this.yTickSize = iTickSize;
19088
19089         var tickMap = {};
19090
19091         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19092             if (!tickMap[i]) {
19093                 this.yTicks[this.yTicks.length] = i;
19094                 tickMap[i] = true;
19095             }
19096         }
19097
19098         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19099             if (!tickMap[i]) {
19100                 this.yTicks[this.yTicks.length] = i;
19101                 tickMap[i] = true;
19102             }
19103         }
19104
19105         this.yTicks.sort(this.DDM.numericSort) ;
19106     },
19107
19108     /**
19109      * By default, the element can be dragged any place on the screen.  Use
19110      * this method to limit the horizontal travel of the element.  Pass in
19111      * 0,0 for the parameters if you want to lock the drag to the y axis.
19112      * @method setXConstraint
19113      * @param {int} iLeft the number of pixels the element can move to the left
19114      * @param {int} iRight the number of pixels the element can move to the
19115      * right
19116      * @param {int} iTickSize optional parameter for specifying that the
19117      * element
19118      * should move iTickSize pixels at a time.
19119      */
19120     setXConstraint: function(iLeft, iRight, iTickSize) {
19121         this.leftConstraint = iLeft;
19122         this.rightConstraint = iRight;
19123
19124         this.minX = this.initPageX - iLeft;
19125         this.maxX = this.initPageX + iRight;
19126         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19127
19128         this.constrainX = true;
19129     },
19130
19131     /**
19132      * Clears any constraints applied to this instance.  Also clears ticks
19133      * since they can't exist independent of a constraint at this time.
19134      * @method clearConstraints
19135      */
19136     clearConstraints: function() {
19137         this.constrainX = false;
19138         this.constrainY = false;
19139         this.clearTicks();
19140     },
19141
19142     /**
19143      * Clears any tick interval defined for this instance
19144      * @method clearTicks
19145      */
19146     clearTicks: function() {
19147         this.xTicks = null;
19148         this.yTicks = null;
19149         this.xTickSize = 0;
19150         this.yTickSize = 0;
19151     },
19152
19153     /**
19154      * By default, the element can be dragged any place on the screen.  Set
19155      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19156      * parameters if you want to lock the drag to the x axis.
19157      * @method setYConstraint
19158      * @param {int} iUp the number of pixels the element can move up
19159      * @param {int} iDown the number of pixels the element can move down
19160      * @param {int} iTickSize optional parameter for specifying that the
19161      * element should move iTickSize pixels at a time.
19162      */
19163     setYConstraint: function(iUp, iDown, iTickSize) {
19164         this.topConstraint = iUp;
19165         this.bottomConstraint = iDown;
19166
19167         this.minY = this.initPageY - iUp;
19168         this.maxY = this.initPageY + iDown;
19169         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19170
19171         this.constrainY = true;
19172
19173     },
19174
19175     /**
19176      * resetConstraints must be called if you manually reposition a dd element.
19177      * @method resetConstraints
19178      * @param {boolean} maintainOffset
19179      */
19180     resetConstraints: function() {
19181
19182
19183         // Maintain offsets if necessary
19184         if (this.initPageX || this.initPageX === 0) {
19185             // figure out how much this thing has moved
19186             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19187             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19188
19189             this.setInitPosition(dx, dy);
19190
19191         // This is the first time we have detected the element's position
19192         } else {
19193             this.setInitPosition();
19194         }
19195
19196         if (this.constrainX) {
19197             this.setXConstraint( this.leftConstraint,
19198                                  this.rightConstraint,
19199                                  this.xTickSize        );
19200         }
19201
19202         if (this.constrainY) {
19203             this.setYConstraint( this.topConstraint,
19204                                  this.bottomConstraint,
19205                                  this.yTickSize         );
19206         }
19207     },
19208
19209     /**
19210      * Normally the drag element is moved pixel by pixel, but we can specify
19211      * that it move a number of pixels at a time.  This method resolves the
19212      * location when we have it set up like this.
19213      * @method getTick
19214      * @param {int} val where we want to place the object
19215      * @param {int[]} tickArray sorted array of valid points
19216      * @return {int} the closest tick
19217      * @private
19218      */
19219     getTick: function(val, tickArray) {
19220
19221         if (!tickArray) {
19222             // If tick interval is not defined, it is effectively 1 pixel,
19223             // so we return the value passed to us.
19224             return val;
19225         } else if (tickArray[0] >= val) {
19226             // The value is lower than the first tick, so we return the first
19227             // tick.
19228             return tickArray[0];
19229         } else {
19230             for (var i=0, len=tickArray.length; i<len; ++i) {
19231                 var next = i + 1;
19232                 if (tickArray[next] && tickArray[next] >= val) {
19233                     var diff1 = val - tickArray[i];
19234                     var diff2 = tickArray[next] - val;
19235                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19236                 }
19237             }
19238
19239             // The value is larger than the last tick, so we return the last
19240             // tick.
19241             return tickArray[tickArray.length - 1];
19242         }
19243     },
19244
19245     /**
19246      * toString method
19247      * @method toString
19248      * @return {string} string representation of the dd obj
19249      */
19250     toString: function() {
19251         return ("DragDrop " + this.id);
19252     }
19253
19254 });
19255
19256 })();
19257 /*
19258  * Based on:
19259  * Ext JS Library 1.1.1
19260  * Copyright(c) 2006-2007, Ext JS, LLC.
19261  *
19262  * Originally Released Under LGPL - original licence link has changed is not relivant.
19263  *
19264  * Fork - LGPL
19265  * <script type="text/javascript">
19266  */
19267
19268
19269 /**
19270  * The drag and drop utility provides a framework for building drag and drop
19271  * applications.  In addition to enabling drag and drop for specific elements,
19272  * the drag and drop elements are tracked by the manager class, and the
19273  * interactions between the various elements are tracked during the drag and
19274  * the implementing code is notified about these important moments.
19275  */
19276
19277 // Only load the library once.  Rewriting the manager class would orphan
19278 // existing drag and drop instances.
19279 if (!Roo.dd.DragDropMgr) {
19280
19281 /**
19282  * @class Roo.dd.DragDropMgr
19283  * DragDropMgr is a singleton that tracks the element interaction for
19284  * all DragDrop items in the window.  Generally, you will not call
19285  * this class directly, but it does have helper methods that could
19286  * be useful in your DragDrop implementations.
19287  * @singleton
19288  */
19289 Roo.dd.DragDropMgr = function() {
19290
19291     var Event = Roo.EventManager;
19292
19293     return {
19294
19295         /**
19296          * Two dimensional Array of registered DragDrop objects.  The first
19297          * dimension is the DragDrop item group, the second the DragDrop
19298          * object.
19299          * @property ids
19300          * @type {string: string}
19301          * @private
19302          * @static
19303          */
19304         ids: {},
19305
19306         /**
19307          * Array of element ids defined as drag handles.  Used to determine
19308          * if the element that generated the mousedown event is actually the
19309          * handle and not the html element itself.
19310          * @property handleIds
19311          * @type {string: string}
19312          * @private
19313          * @static
19314          */
19315         handleIds: {},
19316
19317         /**
19318          * the DragDrop object that is currently being dragged
19319          * @property dragCurrent
19320          * @type DragDrop
19321          * @private
19322          * @static
19323          **/
19324         dragCurrent: null,
19325
19326         /**
19327          * the DragDrop object(s) that are being hovered over
19328          * @property dragOvers
19329          * @type Array
19330          * @private
19331          * @static
19332          */
19333         dragOvers: {},
19334
19335         /**
19336          * the X distance between the cursor and the object being dragged
19337          * @property deltaX
19338          * @type int
19339          * @private
19340          * @static
19341          */
19342         deltaX: 0,
19343
19344         /**
19345          * the Y distance between the cursor and the object being dragged
19346          * @property deltaY
19347          * @type int
19348          * @private
19349          * @static
19350          */
19351         deltaY: 0,
19352
19353         /**
19354          * Flag to determine if we should prevent the default behavior of the
19355          * events we define. By default this is true, but this can be set to
19356          * false if you need the default behavior (not recommended)
19357          * @property preventDefault
19358          * @type boolean
19359          * @static
19360          */
19361         preventDefault: true,
19362
19363         /**
19364          * Flag to determine if we should stop the propagation of the events
19365          * we generate. This is true by default but you may want to set it to
19366          * false if the html element contains other features that require the
19367          * mouse click.
19368          * @property stopPropagation
19369          * @type boolean
19370          * @static
19371          */
19372         stopPropagation: true,
19373
19374         /**
19375          * Internal flag that is set to true when drag and drop has been
19376          * intialized
19377          * @property initialized
19378          * @private
19379          * @static
19380          */
19381         initalized: false,
19382
19383         /**
19384          * All drag and drop can be disabled.
19385          * @property locked
19386          * @private
19387          * @static
19388          */
19389         locked: false,
19390
19391         /**
19392          * Called the first time an element is registered.
19393          * @method init
19394          * @private
19395          * @static
19396          */
19397         init: function() {
19398             this.initialized = true;
19399         },
19400
19401         /**
19402          * In point mode, drag and drop interaction is defined by the
19403          * location of the cursor during the drag/drop
19404          * @property POINT
19405          * @type int
19406          * @static
19407          */
19408         POINT: 0,
19409
19410         /**
19411          * In intersect mode, drag and drop interactio nis defined by the
19412          * overlap of two or more drag and drop objects.
19413          * @property INTERSECT
19414          * @type int
19415          * @static
19416          */
19417         INTERSECT: 1,
19418
19419         /**
19420          * The current drag and drop mode.  Default: POINT
19421          * @property mode
19422          * @type int
19423          * @static
19424          */
19425         mode: 0,
19426
19427         /**
19428          * Runs method on all drag and drop objects
19429          * @method _execOnAll
19430          * @private
19431          * @static
19432          */
19433         _execOnAll: function(sMethod, args) {
19434             for (var i in this.ids) {
19435                 for (var j in this.ids[i]) {
19436                     var oDD = this.ids[i][j];
19437                     if (! this.isTypeOfDD(oDD)) {
19438                         continue;
19439                     }
19440                     oDD[sMethod].apply(oDD, args);
19441                 }
19442             }
19443         },
19444
19445         /**
19446          * Drag and drop initialization.  Sets up the global event handlers
19447          * @method _onLoad
19448          * @private
19449          * @static
19450          */
19451         _onLoad: function() {
19452
19453             this.init();
19454
19455             if (!Roo.isTouch) {
19456                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19457                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19458             }
19459             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19460             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19461             
19462             Event.on(window,   "unload",    this._onUnload, this, true);
19463             Event.on(window,   "resize",    this._onResize, this, true);
19464             // Event.on(window,   "mouseout",    this._test);
19465
19466         },
19467
19468         /**
19469          * Reset constraints on all drag and drop objs
19470          * @method _onResize
19471          * @private
19472          * @static
19473          */
19474         _onResize: function(e) {
19475             this._execOnAll("resetConstraints", []);
19476         },
19477
19478         /**
19479          * Lock all drag and drop functionality
19480          * @method lock
19481          * @static
19482          */
19483         lock: function() { this.locked = true; },
19484
19485         /**
19486          * Unlock all drag and drop functionality
19487          * @method unlock
19488          * @static
19489          */
19490         unlock: function() { this.locked = false; },
19491
19492         /**
19493          * Is drag and drop locked?
19494          * @method isLocked
19495          * @return {boolean} True if drag and drop is locked, false otherwise.
19496          * @static
19497          */
19498         isLocked: function() { return this.locked; },
19499
19500         /**
19501          * Location cache that is set for all drag drop objects when a drag is
19502          * initiated, cleared when the drag is finished.
19503          * @property locationCache
19504          * @private
19505          * @static
19506          */
19507         locationCache: {},
19508
19509         /**
19510          * Set useCache to false if you want to force object the lookup of each
19511          * drag and drop linked element constantly during a drag.
19512          * @property useCache
19513          * @type boolean
19514          * @static
19515          */
19516         useCache: true,
19517
19518         /**
19519          * The number of pixels that the mouse needs to move after the
19520          * mousedown before the drag is initiated.  Default=3;
19521          * @property clickPixelThresh
19522          * @type int
19523          * @static
19524          */
19525         clickPixelThresh: 3,
19526
19527         /**
19528          * The number of milliseconds after the mousedown event to initiate the
19529          * drag if we don't get a mouseup event. Default=1000
19530          * @property clickTimeThresh
19531          * @type int
19532          * @static
19533          */
19534         clickTimeThresh: 350,
19535
19536         /**
19537          * Flag that indicates that either the drag pixel threshold or the
19538          * mousdown time threshold has been met
19539          * @property dragThreshMet
19540          * @type boolean
19541          * @private
19542          * @static
19543          */
19544         dragThreshMet: false,
19545
19546         /**
19547          * Timeout used for the click time threshold
19548          * @property clickTimeout
19549          * @type Object
19550          * @private
19551          * @static
19552          */
19553         clickTimeout: null,
19554
19555         /**
19556          * The X position of the mousedown event stored for later use when a
19557          * drag threshold is met.
19558          * @property startX
19559          * @type int
19560          * @private
19561          * @static
19562          */
19563         startX: 0,
19564
19565         /**
19566          * The Y position of the mousedown event stored for later use when a
19567          * drag threshold is met.
19568          * @property startY
19569          * @type int
19570          * @private
19571          * @static
19572          */
19573         startY: 0,
19574
19575         /**
19576          * Each DragDrop instance must be registered with the DragDropMgr.
19577          * This is executed in DragDrop.init()
19578          * @method regDragDrop
19579          * @param {DragDrop} oDD the DragDrop object to register
19580          * @param {String} sGroup the name of the group this element belongs to
19581          * @static
19582          */
19583         regDragDrop: function(oDD, sGroup) {
19584             if (!this.initialized) { this.init(); }
19585
19586             if (!this.ids[sGroup]) {
19587                 this.ids[sGroup] = {};
19588             }
19589             this.ids[sGroup][oDD.id] = oDD;
19590         },
19591
19592         /**
19593          * Removes the supplied dd instance from the supplied group. Executed
19594          * by DragDrop.removeFromGroup, so don't call this function directly.
19595          * @method removeDDFromGroup
19596          * @private
19597          * @static
19598          */
19599         removeDDFromGroup: function(oDD, sGroup) {
19600             if (!this.ids[sGroup]) {
19601                 this.ids[sGroup] = {};
19602             }
19603
19604             var obj = this.ids[sGroup];
19605             if (obj && obj[oDD.id]) {
19606                 delete obj[oDD.id];
19607             }
19608         },
19609
19610         /**
19611          * Unregisters a drag and drop item.  This is executed in
19612          * DragDrop.unreg, use that method instead of calling this directly.
19613          * @method _remove
19614          * @private
19615          * @static
19616          */
19617         _remove: function(oDD) {
19618             for (var g in oDD.groups) {
19619                 if (g && this.ids[g][oDD.id]) {
19620                     delete this.ids[g][oDD.id];
19621                 }
19622             }
19623             delete this.handleIds[oDD.id];
19624         },
19625
19626         /**
19627          * Each DragDrop handle element must be registered.  This is done
19628          * automatically when executing DragDrop.setHandleElId()
19629          * @method regHandle
19630          * @param {String} sDDId the DragDrop id this element is a handle for
19631          * @param {String} sHandleId the id of the element that is the drag
19632          * handle
19633          * @static
19634          */
19635         regHandle: function(sDDId, sHandleId) {
19636             if (!this.handleIds[sDDId]) {
19637                 this.handleIds[sDDId] = {};
19638             }
19639             this.handleIds[sDDId][sHandleId] = sHandleId;
19640         },
19641
19642         /**
19643          * Utility function to determine if a given element has been
19644          * registered as a drag drop item.
19645          * @method isDragDrop
19646          * @param {String} id the element id to check
19647          * @return {boolean} true if this element is a DragDrop item,
19648          * false otherwise
19649          * @static
19650          */
19651         isDragDrop: function(id) {
19652             return ( this.getDDById(id) ) ? true : false;
19653         },
19654
19655         /**
19656          * Returns the drag and drop instances that are in all groups the
19657          * passed in instance belongs to.
19658          * @method getRelated
19659          * @param {DragDrop} p_oDD the obj to get related data for
19660          * @param {boolean} bTargetsOnly if true, only return targetable objs
19661          * @return {DragDrop[]} the related instances
19662          * @static
19663          */
19664         getRelated: function(p_oDD, bTargetsOnly) {
19665             var oDDs = [];
19666             for (var i in p_oDD.groups) {
19667                 for (j in this.ids[i]) {
19668                     var dd = this.ids[i][j];
19669                     if (! this.isTypeOfDD(dd)) {
19670                         continue;
19671                     }
19672                     if (!bTargetsOnly || dd.isTarget) {
19673                         oDDs[oDDs.length] = dd;
19674                     }
19675                 }
19676             }
19677
19678             return oDDs;
19679         },
19680
19681         /**
19682          * Returns true if the specified dd target is a legal target for
19683          * the specifice drag obj
19684          * @method isLegalTarget
19685          * @param {DragDrop} the drag obj
19686          * @param {DragDrop} the target
19687          * @return {boolean} true if the target is a legal target for the
19688          * dd obj
19689          * @static
19690          */
19691         isLegalTarget: function (oDD, oTargetDD) {
19692             var targets = this.getRelated(oDD, true);
19693             for (var i=0, len=targets.length;i<len;++i) {
19694                 if (targets[i].id == oTargetDD.id) {
19695                     return true;
19696                 }
19697             }
19698
19699             return false;
19700         },
19701
19702         /**
19703          * My goal is to be able to transparently determine if an object is
19704          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19705          * returns "object", oDD.constructor.toString() always returns
19706          * "DragDrop" and not the name of the subclass.  So for now it just
19707          * evaluates a well-known variable in DragDrop.
19708          * @method isTypeOfDD
19709          * @param {Object} the object to evaluate
19710          * @return {boolean} true if typeof oDD = DragDrop
19711          * @static
19712          */
19713         isTypeOfDD: function (oDD) {
19714             return (oDD && oDD.__ygDragDrop);
19715         },
19716
19717         /**
19718          * Utility function to determine if a given element has been
19719          * registered as a drag drop handle for the given Drag Drop object.
19720          * @method isHandle
19721          * @param {String} id the element id to check
19722          * @return {boolean} true if this element is a DragDrop handle, false
19723          * otherwise
19724          * @static
19725          */
19726         isHandle: function(sDDId, sHandleId) {
19727             return ( this.handleIds[sDDId] &&
19728                             this.handleIds[sDDId][sHandleId] );
19729         },
19730
19731         /**
19732          * Returns the DragDrop instance for a given id
19733          * @method getDDById
19734          * @param {String} id the id of the DragDrop object
19735          * @return {DragDrop} the drag drop object, null if it is not found
19736          * @static
19737          */
19738         getDDById: function(id) {
19739             for (var i in this.ids) {
19740                 if (this.ids[i][id]) {
19741                     return this.ids[i][id];
19742                 }
19743             }
19744             return null;
19745         },
19746
19747         /**
19748          * Fired after a registered DragDrop object gets the mousedown event.
19749          * Sets up the events required to track the object being dragged
19750          * @method handleMouseDown
19751          * @param {Event} e the event
19752          * @param oDD the DragDrop object being dragged
19753          * @private
19754          * @static
19755          */
19756         handleMouseDown: function(e, oDD) {
19757             if(Roo.QuickTips){
19758                 Roo.QuickTips.disable();
19759             }
19760             this.currentTarget = e.getTarget();
19761
19762             this.dragCurrent = oDD;
19763
19764             var el = oDD.getEl();
19765
19766             // track start position
19767             this.startX = e.getPageX();
19768             this.startY = e.getPageY();
19769
19770             this.deltaX = this.startX - el.offsetLeft;
19771             this.deltaY = this.startY - el.offsetTop;
19772
19773             this.dragThreshMet = false;
19774
19775             this.clickTimeout = setTimeout(
19776                     function() {
19777                         var DDM = Roo.dd.DDM;
19778                         DDM.startDrag(DDM.startX, DDM.startY);
19779                     },
19780                     this.clickTimeThresh );
19781         },
19782
19783         /**
19784          * Fired when either the drag pixel threshol or the mousedown hold
19785          * time threshold has been met.
19786          * @method startDrag
19787          * @param x {int} the X position of the original mousedown
19788          * @param y {int} the Y position of the original mousedown
19789          * @static
19790          */
19791         startDrag: function(x, y) {
19792             clearTimeout(this.clickTimeout);
19793             if (this.dragCurrent) {
19794                 this.dragCurrent.b4StartDrag(x, y);
19795                 this.dragCurrent.startDrag(x, y);
19796             }
19797             this.dragThreshMet = true;
19798         },
19799
19800         /**
19801          * Internal function to handle the mouseup event.  Will be invoked
19802          * from the context of the document.
19803          * @method handleMouseUp
19804          * @param {Event} e the event
19805          * @private
19806          * @static
19807          */
19808         handleMouseUp: function(e) {
19809
19810             if(Roo.QuickTips){
19811                 Roo.QuickTips.enable();
19812             }
19813             if (! this.dragCurrent) {
19814                 return;
19815             }
19816
19817             clearTimeout(this.clickTimeout);
19818
19819             if (this.dragThreshMet) {
19820                 this.fireEvents(e, true);
19821             } else {
19822             }
19823
19824             this.stopDrag(e);
19825
19826             this.stopEvent(e);
19827         },
19828
19829         /**
19830          * Utility to stop event propagation and event default, if these
19831          * features are turned on.
19832          * @method stopEvent
19833          * @param {Event} e the event as returned by this.getEvent()
19834          * @static
19835          */
19836         stopEvent: function(e){
19837             if(this.stopPropagation) {
19838                 e.stopPropagation();
19839             }
19840
19841             if (this.preventDefault) {
19842                 e.preventDefault();
19843             }
19844         },
19845
19846         /**
19847          * Internal function to clean up event handlers after the drag
19848          * operation is complete
19849          * @method stopDrag
19850          * @param {Event} e the event
19851          * @private
19852          * @static
19853          */
19854         stopDrag: function(e) {
19855             // Fire the drag end event for the item that was dragged
19856             if (this.dragCurrent) {
19857                 if (this.dragThreshMet) {
19858                     this.dragCurrent.b4EndDrag(e);
19859                     this.dragCurrent.endDrag(e);
19860                 }
19861
19862                 this.dragCurrent.onMouseUp(e);
19863             }
19864
19865             this.dragCurrent = null;
19866             this.dragOvers = {};
19867         },
19868
19869         /**
19870          * Internal function to handle the mousemove event.  Will be invoked
19871          * from the context of the html element.
19872          *
19873          * @TODO figure out what we can do about mouse events lost when the
19874          * user drags objects beyond the window boundary.  Currently we can
19875          * detect this in internet explorer by verifying that the mouse is
19876          * down during the mousemove event.  Firefox doesn't give us the
19877          * button state on the mousemove event.
19878          * @method handleMouseMove
19879          * @param {Event} e the event
19880          * @private
19881          * @static
19882          */
19883         handleMouseMove: function(e) {
19884             if (! this.dragCurrent) {
19885                 return true;
19886             }
19887
19888             // var button = e.which || e.button;
19889
19890             // check for IE mouseup outside of page boundary
19891             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19892                 this.stopEvent(e);
19893                 return this.handleMouseUp(e);
19894             }
19895
19896             if (!this.dragThreshMet) {
19897                 var diffX = Math.abs(this.startX - e.getPageX());
19898                 var diffY = Math.abs(this.startY - e.getPageY());
19899                 if (diffX > this.clickPixelThresh ||
19900                             diffY > this.clickPixelThresh) {
19901                     this.startDrag(this.startX, this.startY);
19902                 }
19903             }
19904
19905             if (this.dragThreshMet) {
19906                 this.dragCurrent.b4Drag(e);
19907                 this.dragCurrent.onDrag(e);
19908                 if(!this.dragCurrent.moveOnly){
19909                     this.fireEvents(e, false);
19910                 }
19911             }
19912
19913             this.stopEvent(e);
19914
19915             return true;
19916         },
19917
19918         /**
19919          * Iterates over all of the DragDrop elements to find ones we are
19920          * hovering over or dropping on
19921          * @method fireEvents
19922          * @param {Event} e the event
19923          * @param {boolean} isDrop is this a drop op or a mouseover op?
19924          * @private
19925          * @static
19926          */
19927         fireEvents: function(e, isDrop) {
19928             var dc = this.dragCurrent;
19929
19930             // If the user did the mouse up outside of the window, we could
19931             // get here even though we have ended the drag.
19932             if (!dc || dc.isLocked()) {
19933                 return;
19934             }
19935
19936             var pt = e.getPoint();
19937
19938             // cache the previous dragOver array
19939             var oldOvers = [];
19940
19941             var outEvts   = [];
19942             var overEvts  = [];
19943             var dropEvts  = [];
19944             var enterEvts = [];
19945
19946             // Check to see if the object(s) we were hovering over is no longer
19947             // being hovered over so we can fire the onDragOut event
19948             for (var i in this.dragOvers) {
19949
19950                 var ddo = this.dragOvers[i];
19951
19952                 if (! this.isTypeOfDD(ddo)) {
19953                     continue;
19954                 }
19955
19956                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19957                     outEvts.push( ddo );
19958                 }
19959
19960                 oldOvers[i] = true;
19961                 delete this.dragOvers[i];
19962             }
19963
19964             for (var sGroup in dc.groups) {
19965
19966                 if ("string" != typeof sGroup) {
19967                     continue;
19968                 }
19969
19970                 for (i in this.ids[sGroup]) {
19971                     var oDD = this.ids[sGroup][i];
19972                     if (! this.isTypeOfDD(oDD)) {
19973                         continue;
19974                     }
19975
19976                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19977                         if (this.isOverTarget(pt, oDD, this.mode)) {
19978                             // look for drop interactions
19979                             if (isDrop) {
19980                                 dropEvts.push( oDD );
19981                             // look for drag enter and drag over interactions
19982                             } else {
19983
19984                                 // initial drag over: dragEnter fires
19985                                 if (!oldOvers[oDD.id]) {
19986                                     enterEvts.push( oDD );
19987                                 // subsequent drag overs: dragOver fires
19988                                 } else {
19989                                     overEvts.push( oDD );
19990                                 }
19991
19992                                 this.dragOvers[oDD.id] = oDD;
19993                             }
19994                         }
19995                     }
19996                 }
19997             }
19998
19999             if (this.mode) {
20000                 if (outEvts.length) {
20001                     dc.b4DragOut(e, outEvts);
20002                     dc.onDragOut(e, outEvts);
20003                 }
20004
20005                 if (enterEvts.length) {
20006                     dc.onDragEnter(e, enterEvts);
20007                 }
20008
20009                 if (overEvts.length) {
20010                     dc.b4DragOver(e, overEvts);
20011                     dc.onDragOver(e, overEvts);
20012                 }
20013
20014                 if (dropEvts.length) {
20015                     dc.b4DragDrop(e, dropEvts);
20016                     dc.onDragDrop(e, dropEvts);
20017                 }
20018
20019             } else {
20020                 // fire dragout events
20021                 var len = 0;
20022                 for (i=0, len=outEvts.length; i<len; ++i) {
20023                     dc.b4DragOut(e, outEvts[i].id);
20024                     dc.onDragOut(e, outEvts[i].id);
20025                 }
20026
20027                 // fire enter events
20028                 for (i=0,len=enterEvts.length; i<len; ++i) {
20029                     // dc.b4DragEnter(e, oDD.id);
20030                     dc.onDragEnter(e, enterEvts[i].id);
20031                 }
20032
20033                 // fire over events
20034                 for (i=0,len=overEvts.length; i<len; ++i) {
20035                     dc.b4DragOver(e, overEvts[i].id);
20036                     dc.onDragOver(e, overEvts[i].id);
20037                 }
20038
20039                 // fire drop events
20040                 for (i=0, len=dropEvts.length; i<len; ++i) {
20041                     dc.b4DragDrop(e, dropEvts[i].id);
20042                     dc.onDragDrop(e, dropEvts[i].id);
20043                 }
20044
20045             }
20046
20047             // notify about a drop that did not find a target
20048             if (isDrop && !dropEvts.length) {
20049                 dc.onInvalidDrop(e);
20050             }
20051
20052         },
20053
20054         /**
20055          * Helper function for getting the best match from the list of drag
20056          * and drop objects returned by the drag and drop events when we are
20057          * in INTERSECT mode.  It returns either the first object that the
20058          * cursor is over, or the object that has the greatest overlap with
20059          * the dragged element.
20060          * @method getBestMatch
20061          * @param  {DragDrop[]} dds The array of drag and drop objects
20062          * targeted
20063          * @return {DragDrop}       The best single match
20064          * @static
20065          */
20066         getBestMatch: function(dds) {
20067             var winner = null;
20068             // Return null if the input is not what we expect
20069             //if (!dds || !dds.length || dds.length == 0) {
20070                // winner = null;
20071             // If there is only one item, it wins
20072             //} else if (dds.length == 1) {
20073
20074             var len = dds.length;
20075
20076             if (len == 1) {
20077                 winner = dds[0];
20078             } else {
20079                 // Loop through the targeted items
20080                 for (var i=0; i<len; ++i) {
20081                     var dd = dds[i];
20082                     // If the cursor is over the object, it wins.  If the
20083                     // cursor is over multiple matches, the first one we come
20084                     // to wins.
20085                     if (dd.cursorIsOver) {
20086                         winner = dd;
20087                         break;
20088                     // Otherwise the object with the most overlap wins
20089                     } else {
20090                         if (!winner ||
20091                             winner.overlap.getArea() < dd.overlap.getArea()) {
20092                             winner = dd;
20093                         }
20094                     }
20095                 }
20096             }
20097
20098             return winner;
20099         },
20100
20101         /**
20102          * Refreshes the cache of the top-left and bottom-right points of the
20103          * drag and drop objects in the specified group(s).  This is in the
20104          * format that is stored in the drag and drop instance, so typical
20105          * usage is:
20106          * <code>
20107          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20108          * </code>
20109          * Alternatively:
20110          * <code>
20111          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20112          * </code>
20113          * @TODO this really should be an indexed array.  Alternatively this
20114          * method could accept both.
20115          * @method refreshCache
20116          * @param {Object} groups an associative array of groups to refresh
20117          * @static
20118          */
20119         refreshCache: function(groups) {
20120             for (var sGroup in groups) {
20121                 if ("string" != typeof sGroup) {
20122                     continue;
20123                 }
20124                 for (var i in this.ids[sGroup]) {
20125                     var oDD = this.ids[sGroup][i];
20126
20127                     if (this.isTypeOfDD(oDD)) {
20128                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20129                         var loc = this.getLocation(oDD);
20130                         if (loc) {
20131                             this.locationCache[oDD.id] = loc;
20132                         } else {
20133                             delete this.locationCache[oDD.id];
20134                             // this will unregister the drag and drop object if
20135                             // the element is not in a usable state
20136                             // oDD.unreg();
20137                         }
20138                     }
20139                 }
20140             }
20141         },
20142
20143         /**
20144          * This checks to make sure an element exists and is in the DOM.  The
20145          * main purpose is to handle cases where innerHTML is used to remove
20146          * drag and drop objects from the DOM.  IE provides an 'unspecified
20147          * error' when trying to access the offsetParent of such an element
20148          * @method verifyEl
20149          * @param {HTMLElement} el the element to check
20150          * @return {boolean} true if the element looks usable
20151          * @static
20152          */
20153         verifyEl: function(el) {
20154             if (el) {
20155                 var parent;
20156                 if(Roo.isIE){
20157                     try{
20158                         parent = el.offsetParent;
20159                     }catch(e){}
20160                 }else{
20161                     parent = el.offsetParent;
20162                 }
20163                 if (parent) {
20164                     return true;
20165                 }
20166             }
20167
20168             return false;
20169         },
20170
20171         /**
20172          * Returns a Region object containing the drag and drop element's position
20173          * and size, including the padding configured for it
20174          * @method getLocation
20175          * @param {DragDrop} oDD the drag and drop object to get the
20176          *                       location for
20177          * @return {Roo.lib.Region} a Region object representing the total area
20178          *                             the element occupies, including any padding
20179          *                             the instance is configured for.
20180          * @static
20181          */
20182         getLocation: function(oDD) {
20183             if (! this.isTypeOfDD(oDD)) {
20184                 return null;
20185             }
20186
20187             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20188
20189             try {
20190                 pos= Roo.lib.Dom.getXY(el);
20191             } catch (e) { }
20192
20193             if (!pos) {
20194                 return null;
20195             }
20196
20197             x1 = pos[0];
20198             x2 = x1 + el.offsetWidth;
20199             y1 = pos[1];
20200             y2 = y1 + el.offsetHeight;
20201
20202             t = y1 - oDD.padding[0];
20203             r = x2 + oDD.padding[1];
20204             b = y2 + oDD.padding[2];
20205             l = x1 - oDD.padding[3];
20206
20207             return new Roo.lib.Region( t, r, b, l );
20208         },
20209
20210         /**
20211          * Checks the cursor location to see if it over the target
20212          * @method isOverTarget
20213          * @param {Roo.lib.Point} pt The point to evaluate
20214          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20215          * @return {boolean} true if the mouse is over the target
20216          * @private
20217          * @static
20218          */
20219         isOverTarget: function(pt, oTarget, intersect) {
20220             // use cache if available
20221             var loc = this.locationCache[oTarget.id];
20222             if (!loc || !this.useCache) {
20223                 loc = this.getLocation(oTarget);
20224                 this.locationCache[oTarget.id] = loc;
20225
20226             }
20227
20228             if (!loc) {
20229                 return false;
20230             }
20231
20232             oTarget.cursorIsOver = loc.contains( pt );
20233
20234             // DragDrop is using this as a sanity check for the initial mousedown
20235             // in this case we are done.  In POINT mode, if the drag obj has no
20236             // contraints, we are also done. Otherwise we need to evaluate the
20237             // location of the target as related to the actual location of the
20238             // dragged element.
20239             var dc = this.dragCurrent;
20240             if (!dc || !dc.getTargetCoord ||
20241                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20242                 return oTarget.cursorIsOver;
20243             }
20244
20245             oTarget.overlap = null;
20246
20247             // Get the current location of the drag element, this is the
20248             // location of the mouse event less the delta that represents
20249             // where the original mousedown happened on the element.  We
20250             // need to consider constraints and ticks as well.
20251             var pos = dc.getTargetCoord(pt.x, pt.y);
20252
20253             var el = dc.getDragEl();
20254             var curRegion = new Roo.lib.Region( pos.y,
20255                                                    pos.x + el.offsetWidth,
20256                                                    pos.y + el.offsetHeight,
20257                                                    pos.x );
20258
20259             var overlap = curRegion.intersect(loc);
20260
20261             if (overlap) {
20262                 oTarget.overlap = overlap;
20263                 return (intersect) ? true : oTarget.cursorIsOver;
20264             } else {
20265                 return false;
20266             }
20267         },
20268
20269         /**
20270          * unload event handler
20271          * @method _onUnload
20272          * @private
20273          * @static
20274          */
20275         _onUnload: function(e, me) {
20276             Roo.dd.DragDropMgr.unregAll();
20277         },
20278
20279         /**
20280          * Cleans up the drag and drop events and objects.
20281          * @method unregAll
20282          * @private
20283          * @static
20284          */
20285         unregAll: function() {
20286
20287             if (this.dragCurrent) {
20288                 this.stopDrag();
20289                 this.dragCurrent = null;
20290             }
20291
20292             this._execOnAll("unreg", []);
20293
20294             for (i in this.elementCache) {
20295                 delete this.elementCache[i];
20296             }
20297
20298             this.elementCache = {};
20299             this.ids = {};
20300         },
20301
20302         /**
20303          * A cache of DOM elements
20304          * @property elementCache
20305          * @private
20306          * @static
20307          */
20308         elementCache: {},
20309
20310         /**
20311          * Get the wrapper for the DOM element specified
20312          * @method getElWrapper
20313          * @param {String} id the id of the element to get
20314          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20315          * @private
20316          * @deprecated This wrapper isn't that useful
20317          * @static
20318          */
20319         getElWrapper: function(id) {
20320             var oWrapper = this.elementCache[id];
20321             if (!oWrapper || !oWrapper.el) {
20322                 oWrapper = this.elementCache[id] =
20323                     new this.ElementWrapper(Roo.getDom(id));
20324             }
20325             return oWrapper;
20326         },
20327
20328         /**
20329          * Returns the actual DOM element
20330          * @method getElement
20331          * @param {String} id the id of the elment to get
20332          * @return {Object} The element
20333          * @deprecated use Roo.getDom instead
20334          * @static
20335          */
20336         getElement: function(id) {
20337             return Roo.getDom(id);
20338         },
20339
20340         /**
20341          * Returns the style property for the DOM element (i.e.,
20342          * document.getElById(id).style)
20343          * @method getCss
20344          * @param {String} id the id of the elment to get
20345          * @return {Object} The style property of the element
20346          * @deprecated use Roo.getDom instead
20347          * @static
20348          */
20349         getCss: function(id) {
20350             var el = Roo.getDom(id);
20351             return (el) ? el.style : null;
20352         },
20353
20354         /**
20355          * Inner class for cached elements
20356          * @class DragDropMgr.ElementWrapper
20357          * @for DragDropMgr
20358          * @private
20359          * @deprecated
20360          */
20361         ElementWrapper: function(el) {
20362                 /**
20363                  * The element
20364                  * @property el
20365                  */
20366                 this.el = el || null;
20367                 /**
20368                  * The element id
20369                  * @property id
20370                  */
20371                 this.id = this.el && el.id;
20372                 /**
20373                  * A reference to the style property
20374                  * @property css
20375                  */
20376                 this.css = this.el && el.style;
20377             },
20378
20379         /**
20380          * Returns the X position of an html element
20381          * @method getPosX
20382          * @param el the element for which to get the position
20383          * @return {int} the X coordinate
20384          * @for DragDropMgr
20385          * @deprecated use Roo.lib.Dom.getX instead
20386          * @static
20387          */
20388         getPosX: function(el) {
20389             return Roo.lib.Dom.getX(el);
20390         },
20391
20392         /**
20393          * Returns the Y position of an html element
20394          * @method getPosY
20395          * @param el the element for which to get the position
20396          * @return {int} the Y coordinate
20397          * @deprecated use Roo.lib.Dom.getY instead
20398          * @static
20399          */
20400         getPosY: function(el) {
20401             return Roo.lib.Dom.getY(el);
20402         },
20403
20404         /**
20405          * Swap two nodes.  In IE, we use the native method, for others we
20406          * emulate the IE behavior
20407          * @method swapNode
20408          * @param n1 the first node to swap
20409          * @param n2 the other node to swap
20410          * @static
20411          */
20412         swapNode: function(n1, n2) {
20413             if (n1.swapNode) {
20414                 n1.swapNode(n2);
20415             } else {
20416                 var p = n2.parentNode;
20417                 var s = n2.nextSibling;
20418
20419                 if (s == n1) {
20420                     p.insertBefore(n1, n2);
20421                 } else if (n2 == n1.nextSibling) {
20422                     p.insertBefore(n2, n1);
20423                 } else {
20424                     n1.parentNode.replaceChild(n2, n1);
20425                     p.insertBefore(n1, s);
20426                 }
20427             }
20428         },
20429
20430         /**
20431          * Returns the current scroll position
20432          * @method getScroll
20433          * @private
20434          * @static
20435          */
20436         getScroll: function () {
20437             var t, l, dde=document.documentElement, db=document.body;
20438             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20439                 t = dde.scrollTop;
20440                 l = dde.scrollLeft;
20441             } else if (db) {
20442                 t = db.scrollTop;
20443                 l = db.scrollLeft;
20444             } else {
20445
20446             }
20447             return { top: t, left: l };
20448         },
20449
20450         /**
20451          * Returns the specified element style property
20452          * @method getStyle
20453          * @param {HTMLElement} el          the element
20454          * @param {string}      styleProp   the style property
20455          * @return {string} The value of the style property
20456          * @deprecated use Roo.lib.Dom.getStyle
20457          * @static
20458          */
20459         getStyle: function(el, styleProp) {
20460             return Roo.fly(el).getStyle(styleProp);
20461         },
20462
20463         /**
20464          * Gets the scrollTop
20465          * @method getScrollTop
20466          * @return {int} the document's scrollTop
20467          * @static
20468          */
20469         getScrollTop: function () { return this.getScroll().top; },
20470
20471         /**
20472          * Gets the scrollLeft
20473          * @method getScrollLeft
20474          * @return {int} the document's scrollTop
20475          * @static
20476          */
20477         getScrollLeft: function () { return this.getScroll().left; },
20478
20479         /**
20480          * Sets the x/y position of an element to the location of the
20481          * target element.
20482          * @method moveToEl
20483          * @param {HTMLElement} moveEl      The element to move
20484          * @param {HTMLElement} targetEl    The position reference element
20485          * @static
20486          */
20487         moveToEl: function (moveEl, targetEl) {
20488             var aCoord = Roo.lib.Dom.getXY(targetEl);
20489             Roo.lib.Dom.setXY(moveEl, aCoord);
20490         },
20491
20492         /**
20493          * Numeric array sort function
20494          * @method numericSort
20495          * @static
20496          */
20497         numericSort: function(a, b) { return (a - b); },
20498
20499         /**
20500          * Internal counter
20501          * @property _timeoutCount
20502          * @private
20503          * @static
20504          */
20505         _timeoutCount: 0,
20506
20507         /**
20508          * Trying to make the load order less important.  Without this we get
20509          * an error if this file is loaded before the Event Utility.
20510          * @method _addListeners
20511          * @private
20512          * @static
20513          */
20514         _addListeners: function() {
20515             var DDM = Roo.dd.DDM;
20516             if ( Roo.lib.Event && document ) {
20517                 DDM._onLoad();
20518             } else {
20519                 if (DDM._timeoutCount > 2000) {
20520                 } else {
20521                     setTimeout(DDM._addListeners, 10);
20522                     if (document && document.body) {
20523                         DDM._timeoutCount += 1;
20524                     }
20525                 }
20526             }
20527         },
20528
20529         /**
20530          * Recursively searches the immediate parent and all child nodes for
20531          * the handle element in order to determine wheter or not it was
20532          * clicked.
20533          * @method handleWasClicked
20534          * @param node the html element to inspect
20535          * @static
20536          */
20537         handleWasClicked: function(node, id) {
20538             if (this.isHandle(id, node.id)) {
20539                 return true;
20540             } else {
20541                 // check to see if this is a text node child of the one we want
20542                 var p = node.parentNode;
20543
20544                 while (p) {
20545                     if (this.isHandle(id, p.id)) {
20546                         return true;
20547                     } else {
20548                         p = p.parentNode;
20549                     }
20550                 }
20551             }
20552
20553             return false;
20554         }
20555
20556     };
20557
20558 }();
20559
20560 // shorter alias, save a few bytes
20561 Roo.dd.DDM = Roo.dd.DragDropMgr;
20562 Roo.dd.DDM._addListeners();
20563
20564 }/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574
20575 /**
20576  * @class Roo.dd.DD
20577  * A DragDrop implementation where the linked element follows the
20578  * mouse cursor during a drag.
20579  * @extends Roo.dd.DragDrop
20580  * @constructor
20581  * @param {String} id the id of the linked element
20582  * @param {String} sGroup the group of related DragDrop items
20583  * @param {object} config an object containing configurable attributes
20584  *                Valid properties for DD:
20585  *                    scroll
20586  */
20587 Roo.dd.DD = function(id, sGroup, config) {
20588     if (id) {
20589         this.init(id, sGroup, config);
20590     }
20591 };
20592
20593 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20594
20595     /**
20596      * When set to true, the utility automatically tries to scroll the browser
20597      * window wehn a drag and drop element is dragged near the viewport boundary.
20598      * Defaults to true.
20599      * @property scroll
20600      * @type boolean
20601      */
20602     scroll: true,
20603
20604     /**
20605      * Sets the pointer offset to the distance between the linked element's top
20606      * left corner and the location the element was clicked
20607      * @method autoOffset
20608      * @param {int} iPageX the X coordinate of the click
20609      * @param {int} iPageY the Y coordinate of the click
20610      */
20611     autoOffset: function(iPageX, iPageY) {
20612         var x = iPageX - this.startPageX;
20613         var y = iPageY - this.startPageY;
20614         this.setDelta(x, y);
20615     },
20616
20617     /**
20618      * Sets the pointer offset.  You can call this directly to force the
20619      * offset to be in a particular location (e.g., pass in 0,0 to set it
20620      * to the center of the object)
20621      * @method setDelta
20622      * @param {int} iDeltaX the distance from the left
20623      * @param {int} iDeltaY the distance from the top
20624      */
20625     setDelta: function(iDeltaX, iDeltaY) {
20626         this.deltaX = iDeltaX;
20627         this.deltaY = iDeltaY;
20628     },
20629
20630     /**
20631      * Sets the drag element to the location of the mousedown or click event,
20632      * maintaining the cursor location relative to the location on the element
20633      * that was clicked.  Override this if you want to place the element in a
20634      * location other than where the cursor is.
20635      * @method setDragElPos
20636      * @param {int} iPageX the X coordinate of the mousedown or drag event
20637      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20638      */
20639     setDragElPos: function(iPageX, iPageY) {
20640         // the first time we do this, we are going to check to make sure
20641         // the element has css positioning
20642
20643         var el = this.getDragEl();
20644         this.alignElWithMouse(el, iPageX, iPageY);
20645     },
20646
20647     /**
20648      * Sets the element to the location of the mousedown or click event,
20649      * maintaining the cursor location relative to the location on the element
20650      * that was clicked.  Override this if you want to place the element in a
20651      * location other than where the cursor is.
20652      * @method alignElWithMouse
20653      * @param {HTMLElement} el the element to move
20654      * @param {int} iPageX the X coordinate of the mousedown or drag event
20655      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20656      */
20657     alignElWithMouse: function(el, iPageX, iPageY) {
20658         var oCoord = this.getTargetCoord(iPageX, iPageY);
20659         var fly = el.dom ? el : Roo.fly(el);
20660         if (!this.deltaSetXY) {
20661             var aCoord = [oCoord.x, oCoord.y];
20662             fly.setXY(aCoord);
20663             var newLeft = fly.getLeft(true);
20664             var newTop  = fly.getTop(true);
20665             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20666         } else {
20667             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20668         }
20669
20670         this.cachePosition(oCoord.x, oCoord.y);
20671         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20672         return oCoord;
20673     },
20674
20675     /**
20676      * Saves the most recent position so that we can reset the constraints and
20677      * tick marks on-demand.  We need to know this so that we can calculate the
20678      * number of pixels the element is offset from its original position.
20679      * @method cachePosition
20680      * @param iPageX the current x position (optional, this just makes it so we
20681      * don't have to look it up again)
20682      * @param iPageY the current y position (optional, this just makes it so we
20683      * don't have to look it up again)
20684      */
20685     cachePosition: function(iPageX, iPageY) {
20686         if (iPageX) {
20687             this.lastPageX = iPageX;
20688             this.lastPageY = iPageY;
20689         } else {
20690             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20691             this.lastPageX = aCoord[0];
20692             this.lastPageY = aCoord[1];
20693         }
20694     },
20695
20696     /**
20697      * Auto-scroll the window if the dragged object has been moved beyond the
20698      * visible window boundary.
20699      * @method autoScroll
20700      * @param {int} x the drag element's x position
20701      * @param {int} y the drag element's y position
20702      * @param {int} h the height of the drag element
20703      * @param {int} w the width of the drag element
20704      * @private
20705      */
20706     autoScroll: function(x, y, h, w) {
20707
20708         if (this.scroll) {
20709             // The client height
20710             var clientH = Roo.lib.Dom.getViewWidth();
20711
20712             // The client width
20713             var clientW = Roo.lib.Dom.getViewHeight();
20714
20715             // The amt scrolled down
20716             var st = this.DDM.getScrollTop();
20717
20718             // The amt scrolled right
20719             var sl = this.DDM.getScrollLeft();
20720
20721             // Location of the bottom of the element
20722             var bot = h + y;
20723
20724             // Location of the right of the element
20725             var right = w + x;
20726
20727             // The distance from the cursor to the bottom of the visible area,
20728             // adjusted so that we don't scroll if the cursor is beyond the
20729             // element drag constraints
20730             var toBot = (clientH + st - y - this.deltaY);
20731
20732             // The distance from the cursor to the right of the visible area
20733             var toRight = (clientW + sl - x - this.deltaX);
20734
20735
20736             // How close to the edge the cursor must be before we scroll
20737             // var thresh = (document.all) ? 100 : 40;
20738             var thresh = 40;
20739
20740             // How many pixels to scroll per autoscroll op.  This helps to reduce
20741             // clunky scrolling. IE is more sensitive about this ... it needs this
20742             // value to be higher.
20743             var scrAmt = (document.all) ? 80 : 30;
20744
20745             // Scroll down if we are near the bottom of the visible page and the
20746             // obj extends below the crease
20747             if ( bot > clientH && toBot < thresh ) {
20748                 window.scrollTo(sl, st + scrAmt);
20749             }
20750
20751             // Scroll up if the window is scrolled down and the top of the object
20752             // goes above the top border
20753             if ( y < st && st > 0 && y - st < thresh ) {
20754                 window.scrollTo(sl, st - scrAmt);
20755             }
20756
20757             // Scroll right if the obj is beyond the right border and the cursor is
20758             // near the border.
20759             if ( right > clientW && toRight < thresh ) {
20760                 window.scrollTo(sl + scrAmt, st);
20761             }
20762
20763             // Scroll left if the window has been scrolled to the right and the obj
20764             // extends past the left border
20765             if ( x < sl && sl > 0 && x - sl < thresh ) {
20766                 window.scrollTo(sl - scrAmt, st);
20767             }
20768         }
20769     },
20770
20771     /**
20772      * Finds the location the element should be placed if we want to move
20773      * it to where the mouse location less the click offset would place us.
20774      * @method getTargetCoord
20775      * @param {int} iPageX the X coordinate of the click
20776      * @param {int} iPageY the Y coordinate of the click
20777      * @return an object that contains the coordinates (Object.x and Object.y)
20778      * @private
20779      */
20780     getTargetCoord: function(iPageX, iPageY) {
20781
20782
20783         var x = iPageX - this.deltaX;
20784         var y = iPageY - this.deltaY;
20785
20786         if (this.constrainX) {
20787             if (x < this.minX) { x = this.minX; }
20788             if (x > this.maxX) { x = this.maxX; }
20789         }
20790
20791         if (this.constrainY) {
20792             if (y < this.minY) { y = this.minY; }
20793             if (y > this.maxY) { y = this.maxY; }
20794         }
20795
20796         x = this.getTick(x, this.xTicks);
20797         y = this.getTick(y, this.yTicks);
20798
20799
20800         return {x:x, y:y};
20801     },
20802
20803     /*
20804      * Sets up config options specific to this class. Overrides
20805      * Roo.dd.DragDrop, but all versions of this method through the
20806      * inheritance chain are called
20807      */
20808     applyConfig: function() {
20809         Roo.dd.DD.superclass.applyConfig.call(this);
20810         this.scroll = (this.config.scroll !== false);
20811     },
20812
20813     /*
20814      * Event that fires prior to the onMouseDown event.  Overrides
20815      * Roo.dd.DragDrop.
20816      */
20817     b4MouseDown: function(e) {
20818         // this.resetConstraints();
20819         this.autoOffset(e.getPageX(),
20820                             e.getPageY());
20821     },
20822
20823     /*
20824      * Event that fires prior to the onDrag event.  Overrides
20825      * Roo.dd.DragDrop.
20826      */
20827     b4Drag: function(e) {
20828         this.setDragElPos(e.getPageX(),
20829                             e.getPageY());
20830     },
20831
20832     toString: function() {
20833         return ("DD " + this.id);
20834     }
20835
20836     //////////////////////////////////////////////////////////////////////////
20837     // Debugging ygDragDrop events that can be overridden
20838     //////////////////////////////////////////////////////////////////////////
20839     /*
20840     startDrag: function(x, y) {
20841     },
20842
20843     onDrag: function(e) {
20844     },
20845
20846     onDragEnter: function(e, id) {
20847     },
20848
20849     onDragOver: function(e, id) {
20850     },
20851
20852     onDragOut: function(e, id) {
20853     },
20854
20855     onDragDrop: function(e, id) {
20856     },
20857
20858     endDrag: function(e) {
20859     }
20860
20861     */
20862
20863 });/*
20864  * Based on:
20865  * Ext JS Library 1.1.1
20866  * Copyright(c) 2006-2007, Ext JS, LLC.
20867  *
20868  * Originally Released Under LGPL - original licence link has changed is not relivant.
20869  *
20870  * Fork - LGPL
20871  * <script type="text/javascript">
20872  */
20873
20874 /**
20875  * @class Roo.dd.DDProxy
20876  * A DragDrop implementation that inserts an empty, bordered div into
20877  * the document that follows the cursor during drag operations.  At the time of
20878  * the click, the frame div is resized to the dimensions of the linked html
20879  * element, and moved to the exact location of the linked element.
20880  *
20881  * References to the "frame" element refer to the single proxy element that
20882  * was created to be dragged in place of all DDProxy elements on the
20883  * page.
20884  *
20885  * @extends Roo.dd.DD
20886  * @constructor
20887  * @param {String} id the id of the linked html element
20888  * @param {String} sGroup the group of related DragDrop objects
20889  * @param {object} config an object containing configurable attributes
20890  *                Valid properties for DDProxy in addition to those in DragDrop:
20891  *                   resizeFrame, centerFrame, dragElId
20892  */
20893 Roo.dd.DDProxy = function(id, sGroup, config) {
20894     if (id) {
20895         this.init(id, sGroup, config);
20896         this.initFrame();
20897     }
20898 };
20899
20900 /**
20901  * The default drag frame div id
20902  * @property Roo.dd.DDProxy.dragElId
20903  * @type String
20904  * @static
20905  */
20906 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20907
20908 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20909
20910     /**
20911      * By default we resize the drag frame to be the same size as the element
20912      * we want to drag (this is to get the frame effect).  We can turn it off
20913      * if we want a different behavior.
20914      * @property resizeFrame
20915      * @type boolean
20916      */
20917     resizeFrame: true,
20918
20919     /**
20920      * By default the frame is positioned exactly where the drag element is, so
20921      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20922      * you do not have constraints on the obj is to have the drag frame centered
20923      * around the cursor.  Set centerFrame to true for this effect.
20924      * @property centerFrame
20925      * @type boolean
20926      */
20927     centerFrame: false,
20928
20929     /**
20930      * Creates the proxy element if it does not yet exist
20931      * @method createFrame
20932      */
20933     createFrame: function() {
20934         var self = this;
20935         var body = document.body;
20936
20937         if (!body || !body.firstChild) {
20938             setTimeout( function() { self.createFrame(); }, 50 );
20939             return;
20940         }
20941
20942         var div = this.getDragEl();
20943
20944         if (!div) {
20945             div    = document.createElement("div");
20946             div.id = this.dragElId;
20947             var s  = div.style;
20948
20949             s.position   = "absolute";
20950             s.visibility = "hidden";
20951             s.cursor     = "move";
20952             s.border     = "2px solid #aaa";
20953             s.zIndex     = 999;
20954
20955             // appendChild can blow up IE if invoked prior to the window load event
20956             // while rendering a table.  It is possible there are other scenarios
20957             // that would cause this to happen as well.
20958             body.insertBefore(div, body.firstChild);
20959         }
20960     },
20961
20962     /**
20963      * Initialization for the drag frame element.  Must be called in the
20964      * constructor of all subclasses
20965      * @method initFrame
20966      */
20967     initFrame: function() {
20968         this.createFrame();
20969     },
20970
20971     applyConfig: function() {
20972         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20973
20974         this.resizeFrame = (this.config.resizeFrame !== false);
20975         this.centerFrame = (this.config.centerFrame);
20976         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20977     },
20978
20979     /**
20980      * Resizes the drag frame to the dimensions of the clicked object, positions
20981      * it over the object, and finally displays it
20982      * @method showFrame
20983      * @param {int} iPageX X click position
20984      * @param {int} iPageY Y click position
20985      * @private
20986      */
20987     showFrame: function(iPageX, iPageY) {
20988         var el = this.getEl();
20989         var dragEl = this.getDragEl();
20990         var s = dragEl.style;
20991
20992         this._resizeProxy();
20993
20994         if (this.centerFrame) {
20995             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20996                            Math.round(parseInt(s.height, 10)/2) );
20997         }
20998
20999         this.setDragElPos(iPageX, iPageY);
21000
21001         Roo.fly(dragEl).show();
21002     },
21003
21004     /**
21005      * The proxy is automatically resized to the dimensions of the linked
21006      * element when a drag is initiated, unless resizeFrame is set to false
21007      * @method _resizeProxy
21008      * @private
21009      */
21010     _resizeProxy: function() {
21011         if (this.resizeFrame) {
21012             var el = this.getEl();
21013             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21014         }
21015     },
21016
21017     // overrides Roo.dd.DragDrop
21018     b4MouseDown: function(e) {
21019         var x = e.getPageX();
21020         var y = e.getPageY();
21021         this.autoOffset(x, y);
21022         this.setDragElPos(x, y);
21023     },
21024
21025     // overrides Roo.dd.DragDrop
21026     b4StartDrag: function(x, y) {
21027         // show the drag frame
21028         this.showFrame(x, y);
21029     },
21030
21031     // overrides Roo.dd.DragDrop
21032     b4EndDrag: function(e) {
21033         Roo.fly(this.getDragEl()).hide();
21034     },
21035
21036     // overrides Roo.dd.DragDrop
21037     // By default we try to move the element to the last location of the frame.
21038     // This is so that the default behavior mirrors that of Roo.dd.DD.
21039     endDrag: function(e) {
21040
21041         var lel = this.getEl();
21042         var del = this.getDragEl();
21043
21044         // Show the drag frame briefly so we can get its position
21045         del.style.visibility = "";
21046
21047         this.beforeMove();
21048         // Hide the linked element before the move to get around a Safari
21049         // rendering bug.
21050         lel.style.visibility = "hidden";
21051         Roo.dd.DDM.moveToEl(lel, del);
21052         del.style.visibility = "hidden";
21053         lel.style.visibility = "";
21054
21055         this.afterDrag();
21056     },
21057
21058     beforeMove : function(){
21059
21060     },
21061
21062     afterDrag : function(){
21063
21064     },
21065
21066     toString: function() {
21067         return ("DDProxy " + this.id);
21068     }
21069
21070 });
21071 /*
21072  * Based on:
21073  * Ext JS Library 1.1.1
21074  * Copyright(c) 2006-2007, Ext JS, LLC.
21075  *
21076  * Originally Released Under LGPL - original licence link has changed is not relivant.
21077  *
21078  * Fork - LGPL
21079  * <script type="text/javascript">
21080  */
21081
21082  /**
21083  * @class Roo.dd.DDTarget
21084  * A DragDrop implementation that does not move, but can be a drop
21085  * target.  You would get the same result by simply omitting implementation
21086  * for the event callbacks, but this way we reduce the processing cost of the
21087  * event listener and the callbacks.
21088  * @extends Roo.dd.DragDrop
21089  * @constructor
21090  * @param {String} id the id of the element that is a drop target
21091  * @param {String} sGroup the group of related DragDrop objects
21092  * @param {object} config an object containing configurable attributes
21093  *                 Valid properties for DDTarget in addition to those in
21094  *                 DragDrop:
21095  *                    none
21096  */
21097 Roo.dd.DDTarget = function(id, sGroup, config) {
21098     if (id) {
21099         this.initTarget(id, sGroup, config);
21100     }
21101     if (config && (config.listeners || config.events)) { 
21102         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21103             listeners : config.listeners || {}, 
21104             events : config.events || {} 
21105         });    
21106     }
21107 };
21108
21109 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21110 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21111     toString: function() {
21112         return ("DDTarget " + this.id);
21113     }
21114 });
21115 /*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126
21127 /**
21128  * @class Roo.dd.ScrollManager
21129  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21130  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21131  * @singleton
21132  */
21133 Roo.dd.ScrollManager = function(){
21134     var ddm = Roo.dd.DragDropMgr;
21135     var els = {};
21136     var dragEl = null;
21137     var proc = {};
21138     
21139     
21140     
21141     var onStop = function(e){
21142         dragEl = null;
21143         clearProc();
21144     };
21145     
21146     var triggerRefresh = function(){
21147         if(ddm.dragCurrent){
21148              ddm.refreshCache(ddm.dragCurrent.groups);
21149         }
21150     };
21151     
21152     var doScroll = function(){
21153         if(ddm.dragCurrent){
21154             var dds = Roo.dd.ScrollManager;
21155             if(!dds.animate){
21156                 if(proc.el.scroll(proc.dir, dds.increment)){
21157                     triggerRefresh();
21158                 }
21159             }else{
21160                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21161             }
21162         }
21163     };
21164     
21165     var clearProc = function(){
21166         if(proc.id){
21167             clearInterval(proc.id);
21168         }
21169         proc.id = 0;
21170         proc.el = null;
21171         proc.dir = "";
21172     };
21173     
21174     var startProc = function(el, dir){
21175          Roo.log('scroll startproc');
21176         clearProc();
21177         proc.el = el;
21178         proc.dir = dir;
21179         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21180     };
21181     
21182     var onFire = function(e, isDrop){
21183        
21184         if(isDrop || !ddm.dragCurrent){ return; }
21185         var dds = Roo.dd.ScrollManager;
21186         if(!dragEl || dragEl != ddm.dragCurrent){
21187             dragEl = ddm.dragCurrent;
21188             // refresh regions on drag start
21189             dds.refreshCache();
21190         }
21191         
21192         var xy = Roo.lib.Event.getXY(e);
21193         var pt = new Roo.lib.Point(xy[0], xy[1]);
21194         for(var id in els){
21195             var el = els[id], r = el._region;
21196             if(r && r.contains(pt) && el.isScrollable()){
21197                 if(r.bottom - pt.y <= dds.thresh){
21198                     if(proc.el != el){
21199                         startProc(el, "down");
21200                     }
21201                     return;
21202                 }else if(r.right - pt.x <= dds.thresh){
21203                     if(proc.el != el){
21204                         startProc(el, "left");
21205                     }
21206                     return;
21207                 }else if(pt.y - r.top <= dds.thresh){
21208                     if(proc.el != el){
21209                         startProc(el, "up");
21210                     }
21211                     return;
21212                 }else if(pt.x - r.left <= dds.thresh){
21213                     if(proc.el != el){
21214                         startProc(el, "right");
21215                     }
21216                     return;
21217                 }
21218             }
21219         }
21220         clearProc();
21221     };
21222     
21223     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21224     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21225     
21226     return {
21227         /**
21228          * Registers new overflow element(s) to auto scroll
21229          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21230          */
21231         register : function(el){
21232             if(el instanceof Array){
21233                 for(var i = 0, len = el.length; i < len; i++) {
21234                         this.register(el[i]);
21235                 }
21236             }else{
21237                 el = Roo.get(el);
21238                 els[el.id] = el;
21239             }
21240             Roo.dd.ScrollManager.els = els;
21241         },
21242         
21243         /**
21244          * Unregisters overflow element(s) so they are no longer scrolled
21245          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21246          */
21247         unregister : function(el){
21248             if(el instanceof Array){
21249                 for(var i = 0, len = el.length; i < len; i++) {
21250                         this.unregister(el[i]);
21251                 }
21252             }else{
21253                 el = Roo.get(el);
21254                 delete els[el.id];
21255             }
21256         },
21257         
21258         /**
21259          * The number of pixels from the edge of a container the pointer needs to be to 
21260          * trigger scrolling (defaults to 25)
21261          * @type Number
21262          */
21263         thresh : 25,
21264         
21265         /**
21266          * The number of pixels to scroll in each scroll increment (defaults to 50)
21267          * @type Number
21268          */
21269         increment : 100,
21270         
21271         /**
21272          * The frequency of scrolls in milliseconds (defaults to 500)
21273          * @type Number
21274          */
21275         frequency : 500,
21276         
21277         /**
21278          * True to animate the scroll (defaults to true)
21279          * @type Boolean
21280          */
21281         animate: true,
21282         
21283         /**
21284          * The animation duration in seconds - 
21285          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21286          * @type Number
21287          */
21288         animDuration: .4,
21289         
21290         /**
21291          * Manually trigger a cache refresh.
21292          */
21293         refreshCache : function(){
21294             for(var id in els){
21295                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21296                     els[id]._region = els[id].getRegion();
21297                 }
21298             }
21299         }
21300     };
21301 }();/*
21302  * Based on:
21303  * Ext JS Library 1.1.1
21304  * Copyright(c) 2006-2007, Ext JS, LLC.
21305  *
21306  * Originally Released Under LGPL - original licence link has changed is not relivant.
21307  *
21308  * Fork - LGPL
21309  * <script type="text/javascript">
21310  */
21311  
21312
21313 /**
21314  * @class Roo.dd.Registry
21315  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21316  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21317  * @singleton
21318  */
21319 Roo.dd.Registry = function(){
21320     var elements = {}; 
21321     var handles = {}; 
21322     var autoIdSeed = 0;
21323
21324     var getId = function(el, autogen){
21325         if(typeof el == "string"){
21326             return el;
21327         }
21328         var id = el.id;
21329         if(!id && autogen !== false){
21330             id = "roodd-" + (++autoIdSeed);
21331             el.id = id;
21332         }
21333         return id;
21334     };
21335     
21336     return {
21337     /**
21338      * Register a drag drop element
21339      * @param {String|HTMLElement} element The id or DOM node to register
21340      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21341      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21342      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21343      * populated in the data object (if applicable):
21344      * <pre>
21345 Value      Description<br />
21346 ---------  ------------------------------------------<br />
21347 handles    Array of DOM nodes that trigger dragging<br />
21348            for the element being registered<br />
21349 isHandle   True if the element passed in triggers<br />
21350            dragging itself, else false
21351 </pre>
21352      */
21353         register : function(el, data){
21354             data = data || {};
21355             if(typeof el == "string"){
21356                 el = document.getElementById(el);
21357             }
21358             data.ddel = el;
21359             elements[getId(el)] = data;
21360             if(data.isHandle !== false){
21361                 handles[data.ddel.id] = data;
21362             }
21363             if(data.handles){
21364                 var hs = data.handles;
21365                 for(var i = 0, len = hs.length; i < len; i++){
21366                         handles[getId(hs[i])] = data;
21367                 }
21368             }
21369         },
21370
21371     /**
21372      * Unregister a drag drop element
21373      * @param {String|HTMLElement}  element The id or DOM node to unregister
21374      */
21375         unregister : function(el){
21376             var id = getId(el, false);
21377             var data = elements[id];
21378             if(data){
21379                 delete elements[id];
21380                 if(data.handles){
21381                     var hs = data.handles;
21382                     for(var i = 0, len = hs.length; i < len; i++){
21383                         delete handles[getId(hs[i], false)];
21384                     }
21385                 }
21386             }
21387         },
21388
21389     /**
21390      * Returns the handle registered for a DOM Node by id
21391      * @param {String|HTMLElement} id The DOM node or id to look up
21392      * @return {Object} handle The custom handle data
21393      */
21394         getHandle : function(id){
21395             if(typeof id != "string"){ // must be element?
21396                 id = id.id;
21397             }
21398             return handles[id];
21399         },
21400
21401     /**
21402      * Returns the handle that is registered for the DOM node that is the target of the event
21403      * @param {Event} e The event
21404      * @return {Object} handle The custom handle data
21405      */
21406         getHandleFromEvent : function(e){
21407             var t = Roo.lib.Event.getTarget(e);
21408             return t ? handles[t.id] : null;
21409         },
21410
21411     /**
21412      * Returns a custom data object that is registered for a DOM node by id
21413      * @param {String|HTMLElement} id The DOM node or id to look up
21414      * @return {Object} data The custom data
21415      */
21416         getTarget : function(id){
21417             if(typeof id != "string"){ // must be element?
21418                 id = id.id;
21419             }
21420             return elements[id];
21421         },
21422
21423     /**
21424      * Returns a custom data object that is registered for the DOM node that is the target of the event
21425      * @param {Event} e The event
21426      * @return {Object} data The custom data
21427      */
21428         getTargetFromEvent : function(e){
21429             var t = Roo.lib.Event.getTarget(e);
21430             return t ? elements[t.id] || handles[t.id] : null;
21431         }
21432     };
21433 }();/*
21434  * Based on:
21435  * Ext JS Library 1.1.1
21436  * Copyright(c) 2006-2007, Ext JS, LLC.
21437  *
21438  * Originally Released Under LGPL - original licence link has changed is not relivant.
21439  *
21440  * Fork - LGPL
21441  * <script type="text/javascript">
21442  */
21443  
21444
21445 /**
21446  * @class Roo.dd.StatusProxy
21447  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21448  * default drag proxy used by all Roo.dd components.
21449  * @constructor
21450  * @param {Object} config
21451  */
21452 Roo.dd.StatusProxy = function(config){
21453     Roo.apply(this, config);
21454     this.id = this.id || Roo.id();
21455     this.el = new Roo.Layer({
21456         dh: {
21457             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21458                 {tag: "div", cls: "x-dd-drop-icon"},
21459                 {tag: "div", cls: "x-dd-drag-ghost"}
21460             ]
21461         }, 
21462         shadow: !config || config.shadow !== false
21463     });
21464     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21465     this.dropStatus = this.dropNotAllowed;
21466 };
21467
21468 Roo.dd.StatusProxy.prototype = {
21469     /**
21470      * @cfg {String} dropAllowed
21471      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21472      */
21473     dropAllowed : "x-dd-drop-ok",
21474     /**
21475      * @cfg {String} dropNotAllowed
21476      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21477      */
21478     dropNotAllowed : "x-dd-drop-nodrop",
21479
21480     /**
21481      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21482      * over the current target element.
21483      * @param {String} cssClass The css class for the new drop status indicator image
21484      */
21485     setStatus : function(cssClass){
21486         cssClass = cssClass || this.dropNotAllowed;
21487         if(this.dropStatus != cssClass){
21488             this.el.replaceClass(this.dropStatus, cssClass);
21489             this.dropStatus = cssClass;
21490         }
21491     },
21492
21493     /**
21494      * Resets the status indicator to the default dropNotAllowed value
21495      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21496      */
21497     reset : function(clearGhost){
21498         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21499         this.dropStatus = this.dropNotAllowed;
21500         if(clearGhost){
21501             this.ghost.update("");
21502         }
21503     },
21504
21505     /**
21506      * Updates the contents of the ghost element
21507      * @param {String} html The html that will replace the current innerHTML of the ghost element
21508      */
21509     update : function(html){
21510         if(typeof html == "string"){
21511             this.ghost.update(html);
21512         }else{
21513             this.ghost.update("");
21514             html.style.margin = "0";
21515             this.ghost.dom.appendChild(html);
21516         }
21517         // ensure float = none set?? cant remember why though.
21518         var el = this.ghost.dom.firstChild;
21519                 if(el){
21520                         Roo.fly(el).setStyle('float', 'none');
21521                 }
21522     },
21523     
21524     /**
21525      * Returns the underlying proxy {@link Roo.Layer}
21526      * @return {Roo.Layer} el
21527     */
21528     getEl : function(){
21529         return this.el;
21530     },
21531
21532     /**
21533      * Returns the ghost element
21534      * @return {Roo.Element} el
21535      */
21536     getGhost : function(){
21537         return this.ghost;
21538     },
21539
21540     /**
21541      * Hides the proxy
21542      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21543      */
21544     hide : function(clear){
21545         this.el.hide();
21546         if(clear){
21547             this.reset(true);
21548         }
21549     },
21550
21551     /**
21552      * Stops the repair animation if it's currently running
21553      */
21554     stop : function(){
21555         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21556             this.anim.stop();
21557         }
21558     },
21559
21560     /**
21561      * Displays this proxy
21562      */
21563     show : function(){
21564         this.el.show();
21565     },
21566
21567     /**
21568      * Force the Layer to sync its shadow and shim positions to the element
21569      */
21570     sync : function(){
21571         this.el.sync();
21572     },
21573
21574     /**
21575      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21576      * invalid drop operation by the item being dragged.
21577      * @param {Array} xy The XY position of the element ([x, y])
21578      * @param {Function} callback The function to call after the repair is complete
21579      * @param {Object} scope The scope in which to execute the callback
21580      */
21581     repair : function(xy, callback, scope){
21582         this.callback = callback;
21583         this.scope = scope;
21584         if(xy && this.animRepair !== false){
21585             this.el.addClass("x-dd-drag-repair");
21586             this.el.hideUnders(true);
21587             this.anim = this.el.shift({
21588                 duration: this.repairDuration || .5,
21589                 easing: 'easeOut',
21590                 xy: xy,
21591                 stopFx: true,
21592                 callback: this.afterRepair,
21593                 scope: this
21594             });
21595         }else{
21596             this.afterRepair();
21597         }
21598     },
21599
21600     // private
21601     afterRepair : function(){
21602         this.hide(true);
21603         if(typeof this.callback == "function"){
21604             this.callback.call(this.scope || this);
21605         }
21606         this.callback = null;
21607         this.scope = null;
21608     }
21609 };/*
21610  * Based on:
21611  * Ext JS Library 1.1.1
21612  * Copyright(c) 2006-2007, Ext JS, LLC.
21613  *
21614  * Originally Released Under LGPL - original licence link has changed is not relivant.
21615  *
21616  * Fork - LGPL
21617  * <script type="text/javascript">
21618  */
21619
21620 /**
21621  * @class Roo.dd.DragSource
21622  * @extends Roo.dd.DDProxy
21623  * A simple class that provides the basic implementation needed to make any element draggable.
21624  * @constructor
21625  * @param {String/HTMLElement/Element} el The container element
21626  * @param {Object} config
21627  */
21628 Roo.dd.DragSource = function(el, config){
21629     this.el = Roo.get(el);
21630     this.dragData = {};
21631     
21632     Roo.apply(this, config);
21633     
21634     if(!this.proxy){
21635         this.proxy = new Roo.dd.StatusProxy();
21636     }
21637
21638     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21639           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21640     
21641     this.dragging = false;
21642 };
21643
21644 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21645     /**
21646      * @cfg {String} dropAllowed
21647      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21648      */
21649     dropAllowed : "x-dd-drop-ok",
21650     /**
21651      * @cfg {String} dropNotAllowed
21652      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21653      */
21654     dropNotAllowed : "x-dd-drop-nodrop",
21655
21656     /**
21657      * Returns the data object associated with this drag source
21658      * @return {Object} data An object containing arbitrary data
21659      */
21660     getDragData : function(e){
21661         return this.dragData;
21662     },
21663
21664     // private
21665     onDragEnter : function(e, id){
21666         var target = Roo.dd.DragDropMgr.getDDById(id);
21667         this.cachedTarget = target;
21668         if(this.beforeDragEnter(target, e, id) !== false){
21669             if(target.isNotifyTarget){
21670                 var status = target.notifyEnter(this, e, this.dragData);
21671                 this.proxy.setStatus(status);
21672             }else{
21673                 this.proxy.setStatus(this.dropAllowed);
21674             }
21675             
21676             if(this.afterDragEnter){
21677                 /**
21678                  * An empty function by default, but provided so that you can perform a custom action
21679                  * when the dragged item enters the drop target by providing an implementation.
21680                  * @param {Roo.dd.DragDrop} target The drop target
21681                  * @param {Event} e The event object
21682                  * @param {String} id The id of the dragged element
21683                  * @method afterDragEnter
21684                  */
21685                 this.afterDragEnter(target, e, id);
21686             }
21687         }
21688     },
21689
21690     /**
21691      * An empty function by default, but provided so that you can perform a custom action
21692      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21693      * @param {Roo.dd.DragDrop} target The drop target
21694      * @param {Event} e The event object
21695      * @param {String} id The id of the dragged element
21696      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21697      */
21698     beforeDragEnter : function(target, e, id){
21699         return true;
21700     },
21701
21702     // private
21703     alignElWithMouse: function() {
21704         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21705         this.proxy.sync();
21706     },
21707
21708     // private
21709     onDragOver : function(e, id){
21710         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21711         if(this.beforeDragOver(target, e, id) !== false){
21712             if(target.isNotifyTarget){
21713                 var status = target.notifyOver(this, e, this.dragData);
21714                 this.proxy.setStatus(status);
21715             }
21716
21717             if(this.afterDragOver){
21718                 /**
21719                  * An empty function by default, but provided so that you can perform a custom action
21720                  * while the dragged item is over the drop target by providing an implementation.
21721                  * @param {Roo.dd.DragDrop} target The drop target
21722                  * @param {Event} e The event object
21723                  * @param {String} id The id of the dragged element
21724                  * @method afterDragOver
21725                  */
21726                 this.afterDragOver(target, e, id);
21727             }
21728         }
21729     },
21730
21731     /**
21732      * An empty function by default, but provided so that you can perform a custom action
21733      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21734      * @param {Roo.dd.DragDrop} target The drop target
21735      * @param {Event} e The event object
21736      * @param {String} id The id of the dragged element
21737      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21738      */
21739     beforeDragOver : function(target, e, id){
21740         return true;
21741     },
21742
21743     // private
21744     onDragOut : function(e, id){
21745         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21746         if(this.beforeDragOut(target, e, id) !== false){
21747             if(target.isNotifyTarget){
21748                 target.notifyOut(this, e, this.dragData);
21749             }
21750             this.proxy.reset();
21751             if(this.afterDragOut){
21752                 /**
21753                  * An empty function by default, but provided so that you can perform a custom action
21754                  * after the dragged item is dragged out of the target without dropping.
21755                  * @param {Roo.dd.DragDrop} target The drop target
21756                  * @param {Event} e The event object
21757                  * @param {String} id The id of the dragged element
21758                  * @method afterDragOut
21759                  */
21760                 this.afterDragOut(target, e, id);
21761             }
21762         }
21763         this.cachedTarget = null;
21764     },
21765
21766     /**
21767      * An empty function by default, but provided so that you can perform a custom action before the dragged
21768      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21769      * @param {Roo.dd.DragDrop} target The drop target
21770      * @param {Event} e The event object
21771      * @param {String} id The id of the dragged element
21772      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21773      */
21774     beforeDragOut : function(target, e, id){
21775         return true;
21776     },
21777     
21778     // private
21779     onDragDrop : function(e, id){
21780         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21781         if(this.beforeDragDrop(target, e, id) !== false){
21782             if(target.isNotifyTarget){
21783                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21784                     this.onValidDrop(target, e, id);
21785                 }else{
21786                     this.onInvalidDrop(target, e, id);
21787                 }
21788             }else{
21789                 this.onValidDrop(target, e, id);
21790             }
21791             
21792             if(this.afterDragDrop){
21793                 /**
21794                  * An empty function by default, but provided so that you can perform a custom action
21795                  * after a valid drag drop has occurred by providing an implementation.
21796                  * @param {Roo.dd.DragDrop} target The drop target
21797                  * @param {Event} e The event object
21798                  * @param {String} id The id of the dropped element
21799                  * @method afterDragDrop
21800                  */
21801                 this.afterDragDrop(target, e, id);
21802             }
21803         }
21804         delete this.cachedTarget;
21805     },
21806
21807     /**
21808      * An empty function by default, but provided so that you can perform a custom action before the dragged
21809      * item is dropped onto the target and optionally cancel the onDragDrop.
21810      * @param {Roo.dd.DragDrop} target The drop target
21811      * @param {Event} e The event object
21812      * @param {String} id The id of the dragged element
21813      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21814      */
21815     beforeDragDrop : function(target, e, id){
21816         return true;
21817     },
21818
21819     // private
21820     onValidDrop : function(target, e, id){
21821         this.hideProxy();
21822         if(this.afterValidDrop){
21823             /**
21824              * An empty function by default, but provided so that you can perform a custom action
21825              * after a valid drop has occurred by providing an implementation.
21826              * @param {Object} target The target DD 
21827              * @param {Event} e The event object
21828              * @param {String} id The id of the dropped element
21829              * @method afterInvalidDrop
21830              */
21831             this.afterValidDrop(target, e, id);
21832         }
21833     },
21834
21835     // private
21836     getRepairXY : function(e, data){
21837         return this.el.getXY();  
21838     },
21839
21840     // private
21841     onInvalidDrop : function(target, e, id){
21842         this.beforeInvalidDrop(target, e, id);
21843         if(this.cachedTarget){
21844             if(this.cachedTarget.isNotifyTarget){
21845                 this.cachedTarget.notifyOut(this, e, this.dragData);
21846             }
21847             this.cacheTarget = null;
21848         }
21849         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21850
21851         if(this.afterInvalidDrop){
21852             /**
21853              * An empty function by default, but provided so that you can perform a custom action
21854              * after an invalid drop has occurred by providing an implementation.
21855              * @param {Event} e The event object
21856              * @param {String} id The id of the dropped element
21857              * @method afterInvalidDrop
21858              */
21859             this.afterInvalidDrop(e, id);
21860         }
21861     },
21862
21863     // private
21864     afterRepair : function(){
21865         if(Roo.enableFx){
21866             this.el.highlight(this.hlColor || "c3daf9");
21867         }
21868         this.dragging = false;
21869     },
21870
21871     /**
21872      * An empty function by default, but provided so that you can perform a custom action after an invalid
21873      * drop has occurred.
21874      * @param {Roo.dd.DragDrop} target The drop target
21875      * @param {Event} e The event object
21876      * @param {String} id The id of the dragged element
21877      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21878      */
21879     beforeInvalidDrop : function(target, e, id){
21880         return true;
21881     },
21882
21883     // private
21884     handleMouseDown : function(e){
21885         if(this.dragging) {
21886             return;
21887         }
21888         var data = this.getDragData(e);
21889         if(data && this.onBeforeDrag(data, e) !== false){
21890             this.dragData = data;
21891             this.proxy.stop();
21892             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21893         } 
21894     },
21895
21896     /**
21897      * An empty function by default, but provided so that you can perform a custom action before the initial
21898      * drag event begins and optionally cancel it.
21899      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21900      * @param {Event} e The event object
21901      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21902      */
21903     onBeforeDrag : function(data, e){
21904         return true;
21905     },
21906
21907     /**
21908      * An empty function by default, but provided so that you can perform a custom action once the initial
21909      * drag event has begun.  The drag cannot be canceled from this function.
21910      * @param {Number} x The x position of the click on the dragged object
21911      * @param {Number} y The y position of the click on the dragged object
21912      */
21913     onStartDrag : Roo.emptyFn,
21914
21915     // private - YUI override
21916     startDrag : function(x, y){
21917         this.proxy.reset();
21918         this.dragging = true;
21919         this.proxy.update("");
21920         this.onInitDrag(x, y);
21921         this.proxy.show();
21922     },
21923
21924     // private
21925     onInitDrag : function(x, y){
21926         var clone = this.el.dom.cloneNode(true);
21927         clone.id = Roo.id(); // prevent duplicate ids
21928         this.proxy.update(clone);
21929         this.onStartDrag(x, y);
21930         return true;
21931     },
21932
21933     /**
21934      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21935      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21936      */
21937     getProxy : function(){
21938         return this.proxy;  
21939     },
21940
21941     /**
21942      * Hides the drag source's {@link Roo.dd.StatusProxy}
21943      */
21944     hideProxy : function(){
21945         this.proxy.hide();  
21946         this.proxy.reset(true);
21947         this.dragging = false;
21948     },
21949
21950     // private
21951     triggerCacheRefresh : function(){
21952         Roo.dd.DDM.refreshCache(this.groups);
21953     },
21954
21955     // private - override to prevent hiding
21956     b4EndDrag: function(e) {
21957     },
21958
21959     // private - override to prevent moving
21960     endDrag : function(e){
21961         this.onEndDrag(this.dragData, e);
21962     },
21963
21964     // private
21965     onEndDrag : function(data, e){
21966     },
21967     
21968     // private - pin to cursor
21969     autoOffset : function(x, y) {
21970         this.setDelta(-12, -20);
21971     }    
21972 });/*
21973  * Based on:
21974  * Ext JS Library 1.1.1
21975  * Copyright(c) 2006-2007, Ext JS, LLC.
21976  *
21977  * Originally Released Under LGPL - original licence link has changed is not relivant.
21978  *
21979  * Fork - LGPL
21980  * <script type="text/javascript">
21981  */
21982
21983
21984 /**
21985  * @class Roo.dd.DropTarget
21986  * @extends Roo.dd.DDTarget
21987  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21988  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21989  * @constructor
21990  * @param {String/HTMLElement/Element} el The container element
21991  * @param {Object} config
21992  */
21993 Roo.dd.DropTarget = function(el, config){
21994     this.el = Roo.get(el);
21995     
21996     var listeners = false; ;
21997     if (config && config.listeners) {
21998         listeners= config.listeners;
21999         delete config.listeners;
22000     }
22001     Roo.apply(this, config);
22002     
22003     if(this.containerScroll){
22004         Roo.dd.ScrollManager.register(this.el);
22005     }
22006     this.addEvents( {
22007          /**
22008          * @scope Roo.dd.DropTarget
22009          */
22010          
22011          /**
22012          * @event enter
22013          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22014          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22015          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22016          * 
22017          * IMPORTANT : it should set this.overClass and this.dropAllowed
22018          * 
22019          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22020          * @param {Event} e The event
22021          * @param {Object} data An object containing arbitrary data supplied by the drag source
22022          */
22023         "enter" : true,
22024         
22025          /**
22026          * @event over
22027          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22028          * This method will be called on every mouse movement while the drag source is over the drop target.
22029          * This default implementation simply returns the dropAllowed config value.
22030          * 
22031          * IMPORTANT : it should set this.dropAllowed
22032          * 
22033          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22034          * @param {Event} e The event
22035          * @param {Object} data An object containing arbitrary data supplied by the drag source
22036          
22037          */
22038         "over" : true,
22039         /**
22040          * @event out
22041          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22042          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22043          * overClass (if any) from the drop element.
22044          * 
22045          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22046          * @param {Event} e The event
22047          * @param {Object} data An object containing arbitrary data supplied by the drag source
22048          */
22049          "out" : true,
22050          
22051         /**
22052          * @event drop
22053          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22054          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22055          * implementation that does something to process the drop event and returns true so that the drag source's
22056          * repair action does not run.
22057          * 
22058          * IMPORTANT : it should set this.success
22059          * 
22060          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22061          * @param {Event} e The event
22062          * @param {Object} data An object containing arbitrary data supplied by the drag source
22063         */
22064          "drop" : true
22065     });
22066             
22067      
22068     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22069         this.el.dom, 
22070         this.ddGroup || this.group,
22071         {
22072             isTarget: true,
22073             listeners : listeners || {} 
22074            
22075         
22076         }
22077     );
22078
22079 };
22080
22081 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22082     /**
22083      * @cfg {String} overClass
22084      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22085      */
22086      /**
22087      * @cfg {String} ddGroup
22088      * The drag drop group to handle drop events for
22089      */
22090      
22091     /**
22092      * @cfg {String} dropAllowed
22093      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22094      */
22095     dropAllowed : "x-dd-drop-ok",
22096     /**
22097      * @cfg {String} dropNotAllowed
22098      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22099      */
22100     dropNotAllowed : "x-dd-drop-nodrop",
22101     /**
22102      * @cfg {boolean} success
22103      * set this after drop listener.. 
22104      */
22105     success : false,
22106     /**
22107      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22108      * if the drop point is valid for over/enter..
22109      */
22110     valid : false,
22111     // private
22112     isTarget : true,
22113
22114     // private
22115     isNotifyTarget : true,
22116     
22117     /**
22118      * @hide
22119      */
22120     notifyEnter : function(dd, e, data)
22121     {
22122         this.valid = true;
22123         this.fireEvent('enter', dd, e, data);
22124         if(this.overClass){
22125             this.el.addClass(this.overClass);
22126         }
22127         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22128             this.valid ? this.dropAllowed : this.dropNotAllowed
22129         );
22130     },
22131
22132     /**
22133      * @hide
22134      */
22135     notifyOver : function(dd, e, data)
22136     {
22137         this.valid = true;
22138         this.fireEvent('over', dd, e, data);
22139         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22140             this.valid ? this.dropAllowed : this.dropNotAllowed
22141         );
22142     },
22143
22144     /**
22145      * @hide
22146      */
22147     notifyOut : function(dd, e, data)
22148     {
22149         this.fireEvent('out', dd, e, data);
22150         if(this.overClass){
22151             this.el.removeClass(this.overClass);
22152         }
22153     },
22154
22155     /**
22156      * @hide
22157      */
22158     notifyDrop : function(dd, e, data)
22159     {
22160         this.success = false;
22161         this.fireEvent('drop', dd, e, data);
22162         return this.success;
22163     }
22164 });/*
22165  * Based on:
22166  * Ext JS Library 1.1.1
22167  * Copyright(c) 2006-2007, Ext JS, LLC.
22168  *
22169  * Originally Released Under LGPL - original licence link has changed is not relivant.
22170  *
22171  * Fork - LGPL
22172  * <script type="text/javascript">
22173  */
22174
22175
22176 /**
22177  * @class Roo.dd.DragZone
22178  * @extends Roo.dd.DragSource
22179  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22180  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22181  * @constructor
22182  * @param {String/HTMLElement/Element} el The container element
22183  * @param {Object} config
22184  */
22185 Roo.dd.DragZone = function(el, config){
22186     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22187     if(this.containerScroll){
22188         Roo.dd.ScrollManager.register(this.el);
22189     }
22190 };
22191
22192 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22193     /**
22194      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22195      * for auto scrolling during drag operations.
22196      */
22197     /**
22198      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22199      * method after a failed drop (defaults to "c3daf9" - light blue)
22200      */
22201
22202     /**
22203      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22204      * for a valid target to drag based on the mouse down. Override this method
22205      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22206      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22207      * @param {EventObject} e The mouse down event
22208      * @return {Object} The dragData
22209      */
22210     getDragData : function(e){
22211         return Roo.dd.Registry.getHandleFromEvent(e);
22212     },
22213     
22214     /**
22215      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22216      * this.dragData.ddel
22217      * @param {Number} x The x position of the click on the dragged object
22218      * @param {Number} y The y position of the click on the dragged object
22219      * @return {Boolean} true to continue the drag, false to cancel
22220      */
22221     onInitDrag : function(x, y){
22222         this.proxy.update(this.dragData.ddel.cloneNode(true));
22223         this.onStartDrag(x, y);
22224         return true;
22225     },
22226     
22227     /**
22228      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22229      */
22230     afterRepair : function(){
22231         if(Roo.enableFx){
22232             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22233         }
22234         this.dragging = false;
22235     },
22236
22237     /**
22238      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22239      * the XY of this.dragData.ddel
22240      * @param {EventObject} e The mouse up event
22241      * @return {Array} The xy location (e.g. [100, 200])
22242      */
22243     getRepairXY : function(e){
22244         return Roo.Element.fly(this.dragData.ddel).getXY();  
22245     }
22246 });/*
22247  * Based on:
22248  * Ext JS Library 1.1.1
22249  * Copyright(c) 2006-2007, Ext JS, LLC.
22250  *
22251  * Originally Released Under LGPL - original licence link has changed is not relivant.
22252  *
22253  * Fork - LGPL
22254  * <script type="text/javascript">
22255  */
22256 /**
22257  * @class Roo.dd.DropZone
22258  * @extends Roo.dd.DropTarget
22259  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22260  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22261  * @constructor
22262  * @param {String/HTMLElement/Element} el The container element
22263  * @param {Object} config
22264  */
22265 Roo.dd.DropZone = function(el, config){
22266     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22267 };
22268
22269 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22270     /**
22271      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22272      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22273      * provide your own custom lookup.
22274      * @param {Event} e The event
22275      * @return {Object} data The custom data
22276      */
22277     getTargetFromEvent : function(e){
22278         return Roo.dd.Registry.getTargetFromEvent(e);
22279     },
22280
22281     /**
22282      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22283      * that it has registered.  This method has no default implementation and should be overridden to provide
22284      * node-specific processing if necessary.
22285      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22286      * {@link #getTargetFromEvent} for this node)
22287      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22288      * @param {Event} e The event
22289      * @param {Object} data An object containing arbitrary data supplied by the drag source
22290      */
22291     onNodeEnter : function(n, dd, e, data){
22292         
22293     },
22294
22295     /**
22296      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22297      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22298      * overridden to provide the proper feedback.
22299      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22300      * {@link #getTargetFromEvent} for this node)
22301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22302      * @param {Event} e The event
22303      * @param {Object} data An object containing arbitrary data supplied by the drag source
22304      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22305      * underlying {@link Roo.dd.StatusProxy} can be updated
22306      */
22307     onNodeOver : function(n, dd, e, data){
22308         return this.dropAllowed;
22309     },
22310
22311     /**
22312      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22313      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22314      * node-specific processing if necessary.
22315      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22316      * {@link #getTargetFromEvent} for this node)
22317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22318      * @param {Event} e The event
22319      * @param {Object} data An object containing arbitrary data supplied by the drag source
22320      */
22321     onNodeOut : function(n, dd, e, data){
22322         
22323     },
22324
22325     /**
22326      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22327      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22328      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22329      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22330      * {@link #getTargetFromEvent} for this node)
22331      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22332      * @param {Event} e The event
22333      * @param {Object} data An object containing arbitrary data supplied by the drag source
22334      * @return {Boolean} True if the drop was valid, else false
22335      */
22336     onNodeDrop : function(n, dd, e, data){
22337         return false;
22338     },
22339
22340     /**
22341      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22342      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22343      * it should be overridden to provide the proper feedback if necessary.
22344      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22345      * @param {Event} e The event
22346      * @param {Object} data An object containing arbitrary data supplied by the drag source
22347      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22348      * underlying {@link Roo.dd.StatusProxy} can be updated
22349      */
22350     onContainerOver : function(dd, e, data){
22351         return this.dropNotAllowed;
22352     },
22353
22354     /**
22355      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22356      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22357      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22358      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22360      * @param {Event} e The event
22361      * @param {Object} data An object containing arbitrary data supplied by the drag source
22362      * @return {Boolean} True if the drop was valid, else false
22363      */
22364     onContainerDrop : function(dd, e, data){
22365         return false;
22366     },
22367
22368     /**
22369      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22370      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22371      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22372      * you should override this method and provide a custom implementation.
22373      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22374      * @param {Event} e The event
22375      * @param {Object} data An object containing arbitrary data supplied by the drag source
22376      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22377      * underlying {@link Roo.dd.StatusProxy} can be updated
22378      */
22379     notifyEnter : function(dd, e, data){
22380         return this.dropNotAllowed;
22381     },
22382
22383     /**
22384      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22385      * This method will be called on every mouse movement while the drag source is over the drop zone.
22386      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22387      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22388      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22389      * registered node, it will call {@link #onContainerOver}.
22390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22391      * @param {Event} e The event
22392      * @param {Object} data An object containing arbitrary data supplied by the drag source
22393      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22394      * underlying {@link Roo.dd.StatusProxy} can be updated
22395      */
22396     notifyOver : function(dd, e, data){
22397         var n = this.getTargetFromEvent(e);
22398         if(!n){ // not over valid drop target
22399             if(this.lastOverNode){
22400                 this.onNodeOut(this.lastOverNode, dd, e, data);
22401                 this.lastOverNode = null;
22402             }
22403             return this.onContainerOver(dd, e, data);
22404         }
22405         if(this.lastOverNode != n){
22406             if(this.lastOverNode){
22407                 this.onNodeOut(this.lastOverNode, dd, e, data);
22408             }
22409             this.onNodeEnter(n, dd, e, data);
22410             this.lastOverNode = n;
22411         }
22412         return this.onNodeOver(n, dd, e, data);
22413     },
22414
22415     /**
22416      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22417      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22418      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22419      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22420      * @param {Event} e The event
22421      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22422      */
22423     notifyOut : function(dd, e, data){
22424         if(this.lastOverNode){
22425             this.onNodeOut(this.lastOverNode, dd, e, data);
22426             this.lastOverNode = null;
22427         }
22428     },
22429
22430     /**
22431      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22432      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22433      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22434      * otherwise it will call {@link #onContainerDrop}.
22435      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22436      * @param {Event} e The event
22437      * @param {Object} data An object containing arbitrary data supplied by the drag source
22438      * @return {Boolean} True if the drop was valid, else false
22439      */
22440     notifyDrop : function(dd, e, data){
22441         if(this.lastOverNode){
22442             this.onNodeOut(this.lastOverNode, dd, e, data);
22443             this.lastOverNode = null;
22444         }
22445         var n = this.getTargetFromEvent(e);
22446         return n ?
22447             this.onNodeDrop(n, dd, e, data) :
22448             this.onContainerDrop(dd, e, data);
22449     },
22450
22451     // private
22452     triggerCacheRefresh : function(){
22453         Roo.dd.DDM.refreshCache(this.groups);
22454     }  
22455 });/*
22456  * Based on:
22457  * Ext JS Library 1.1.1
22458  * Copyright(c) 2006-2007, Ext JS, LLC.
22459  *
22460  * Originally Released Under LGPL - original licence link has changed is not relivant.
22461  *
22462  * Fork - LGPL
22463  * <script type="text/javascript">
22464  */
22465
22466
22467 /**
22468  * @class Roo.data.SortTypes
22469  * @singleton
22470  * Defines the default sorting (casting?) comparison functions used when sorting data.
22471  */
22472 Roo.data.SortTypes = {
22473     /**
22474      * Default sort that does nothing
22475      * @param {Mixed} s The value being converted
22476      * @return {Mixed} The comparison value
22477      */
22478     none : function(s){
22479         return s;
22480     },
22481     
22482     /**
22483      * The regular expression used to strip tags
22484      * @type {RegExp}
22485      * @property
22486      */
22487     stripTagsRE : /<\/?[^>]+>/gi,
22488     
22489     /**
22490      * Strips all HTML tags to sort on text only
22491      * @param {Mixed} s The value being converted
22492      * @return {String} The comparison value
22493      */
22494     asText : function(s){
22495         return String(s).replace(this.stripTagsRE, "");
22496     },
22497     
22498     /**
22499      * Strips all HTML tags to sort on text only - Case insensitive
22500      * @param {Mixed} s The value being converted
22501      * @return {String} The comparison value
22502      */
22503     asUCText : function(s){
22504         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22505     },
22506     
22507     /**
22508      * Case insensitive string
22509      * @param {Mixed} s The value being converted
22510      * @return {String} The comparison value
22511      */
22512     asUCString : function(s) {
22513         return String(s).toUpperCase();
22514     },
22515     
22516     /**
22517      * Date sorting
22518      * @param {Mixed} s The value being converted
22519      * @return {Number} The comparison value
22520      */
22521     asDate : function(s) {
22522         if(!s){
22523             return 0;
22524         }
22525         if(s instanceof Date){
22526             return s.getTime();
22527         }
22528         return Date.parse(String(s));
22529     },
22530     
22531     /**
22532      * Float sorting
22533      * @param {Mixed} s The value being converted
22534      * @return {Float} The comparison value
22535      */
22536     asFloat : function(s) {
22537         var val = parseFloat(String(s).replace(/,/g, ""));
22538         if(isNaN(val)) {
22539             val = 0;
22540         }
22541         return val;
22542     },
22543     
22544     /**
22545      * Integer sorting
22546      * @param {Mixed} s The value being converted
22547      * @return {Number} The comparison value
22548      */
22549     asInt : function(s) {
22550         var val = parseInt(String(s).replace(/,/g, ""));
22551         if(isNaN(val)) {
22552             val = 0;
22553         }
22554         return val;
22555     }
22556 };/*
22557  * Based on:
22558  * Ext JS Library 1.1.1
22559  * Copyright(c) 2006-2007, Ext JS, LLC.
22560  *
22561  * Originally Released Under LGPL - original licence link has changed is not relivant.
22562  *
22563  * Fork - LGPL
22564  * <script type="text/javascript">
22565  */
22566
22567 /**
22568 * @class Roo.data.Record
22569  * Instances of this class encapsulate both record <em>definition</em> information, and record
22570  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22571  * to access Records cached in an {@link Roo.data.Store} object.<br>
22572  * <p>
22573  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22574  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22575  * objects.<br>
22576  * <p>
22577  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22578  * @constructor
22579  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22580  * {@link #create}. The parameters are the same.
22581  * @param {Array} data An associative Array of data values keyed by the field name.
22582  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22583  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22584  * not specified an integer id is generated.
22585  */
22586 Roo.data.Record = function(data, id){
22587     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22588     this.data = data;
22589 };
22590
22591 /**
22592  * Generate a constructor for a specific record layout.
22593  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22594  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22595  * Each field definition object may contain the following properties: <ul>
22596  * <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,
22597  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22598  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22599  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22600  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22601  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22602  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22603  * this may be omitted.</p></li>
22604  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22605  * <ul><li>auto (Default, implies no conversion)</li>
22606  * <li>string</li>
22607  * <li>int</li>
22608  * <li>float</li>
22609  * <li>boolean</li>
22610  * <li>date</li></ul></p></li>
22611  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22612  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22613  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22614  * by the Reader into an object that will be stored in the Record. It is passed the
22615  * following parameters:<ul>
22616  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22617  * </ul></p></li>
22618  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22619  * </ul>
22620  * <br>usage:<br><pre><code>
22621 var TopicRecord = Roo.data.Record.create(
22622     {name: 'title', mapping: 'topic_title'},
22623     {name: 'author', mapping: 'username'},
22624     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22625     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22626     {name: 'lastPoster', mapping: 'user2'},
22627     {name: 'excerpt', mapping: 'post_text'}
22628 );
22629
22630 var myNewRecord = new TopicRecord({
22631     title: 'Do my job please',
22632     author: 'noobie',
22633     totalPosts: 1,
22634     lastPost: new Date(),
22635     lastPoster: 'Animal',
22636     excerpt: 'No way dude!'
22637 });
22638 myStore.add(myNewRecord);
22639 </code></pre>
22640  * @method create
22641  * @static
22642  */
22643 Roo.data.Record.create = function(o){
22644     var f = function(){
22645         f.superclass.constructor.apply(this, arguments);
22646     };
22647     Roo.extend(f, Roo.data.Record);
22648     var p = f.prototype;
22649     p.fields = new Roo.util.MixedCollection(false, function(field){
22650         return field.name;
22651     });
22652     for(var i = 0, len = o.length; i < len; i++){
22653         p.fields.add(new Roo.data.Field(o[i]));
22654     }
22655     f.getField = function(name){
22656         return p.fields.get(name);  
22657     };
22658     return f;
22659 };
22660
22661 Roo.data.Record.AUTO_ID = 1000;
22662 Roo.data.Record.EDIT = 'edit';
22663 Roo.data.Record.REJECT = 'reject';
22664 Roo.data.Record.COMMIT = 'commit';
22665
22666 Roo.data.Record.prototype = {
22667     /**
22668      * Readonly flag - true if this record has been modified.
22669      * @type Boolean
22670      */
22671     dirty : false,
22672     editing : false,
22673     error: null,
22674     modified: null,
22675
22676     // private
22677     join : function(store){
22678         this.store = store;
22679     },
22680
22681     /**
22682      * Set the named field to the specified value.
22683      * @param {String} name The name of the field to set.
22684      * @param {Object} value The value to set the field to.
22685      */
22686     set : function(name, value){
22687         if(this.data[name] == value){
22688             return;
22689         }
22690         this.dirty = true;
22691         if(!this.modified){
22692             this.modified = {};
22693         }
22694         if(typeof this.modified[name] == 'undefined'){
22695             this.modified[name] = this.data[name];
22696         }
22697         this.data[name] = value;
22698         if(!this.editing && this.store){
22699             this.store.afterEdit(this);
22700         }       
22701     },
22702
22703     /**
22704      * Get the value of the named field.
22705      * @param {String} name The name of the field to get the value of.
22706      * @return {Object} The value of the field.
22707      */
22708     get : function(name){
22709         return this.data[name]; 
22710     },
22711
22712     // private
22713     beginEdit : function(){
22714         this.editing = true;
22715         this.modified = {}; 
22716     },
22717
22718     // private
22719     cancelEdit : function(){
22720         this.editing = false;
22721         delete this.modified;
22722     },
22723
22724     // private
22725     endEdit : function(){
22726         this.editing = false;
22727         if(this.dirty && this.store){
22728             this.store.afterEdit(this);
22729         }
22730     },
22731
22732     /**
22733      * Usually called by the {@link Roo.data.Store} which owns the Record.
22734      * Rejects all changes made to the Record since either creation, or the last commit operation.
22735      * Modified fields are reverted to their original values.
22736      * <p>
22737      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22738      * of reject operations.
22739      */
22740     reject : function(){
22741         var m = this.modified;
22742         for(var n in m){
22743             if(typeof m[n] != "function"){
22744                 this.data[n] = m[n];
22745             }
22746         }
22747         this.dirty = false;
22748         delete this.modified;
22749         this.editing = false;
22750         if(this.store){
22751             this.store.afterReject(this);
22752         }
22753     },
22754
22755     /**
22756      * Usually called by the {@link Roo.data.Store} which owns the Record.
22757      * Commits all changes made to the Record since either creation, or the last commit operation.
22758      * <p>
22759      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22760      * of commit operations.
22761      */
22762     commit : function(){
22763         this.dirty = false;
22764         delete this.modified;
22765         this.editing = false;
22766         if(this.store){
22767             this.store.afterCommit(this);
22768         }
22769     },
22770
22771     // private
22772     hasError : function(){
22773         return this.error != null;
22774     },
22775
22776     // private
22777     clearError : function(){
22778         this.error = null;
22779     },
22780
22781     /**
22782      * Creates a copy of this record.
22783      * @param {String} id (optional) A new record id if you don't want to use this record's id
22784      * @return {Record}
22785      */
22786     copy : function(newId) {
22787         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22788     }
22789 };/*
22790  * Based on:
22791  * Ext JS Library 1.1.1
22792  * Copyright(c) 2006-2007, Ext JS, LLC.
22793  *
22794  * Originally Released Under LGPL - original licence link has changed is not relivant.
22795  *
22796  * Fork - LGPL
22797  * <script type="text/javascript">
22798  */
22799
22800
22801
22802 /**
22803  * @class Roo.data.Store
22804  * @extends Roo.util.Observable
22805  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22806  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22807  * <p>
22808  * 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
22809  * has no knowledge of the format of the data returned by the Proxy.<br>
22810  * <p>
22811  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22812  * instances from the data object. These records are cached and made available through accessor functions.
22813  * @constructor
22814  * Creates a new Store.
22815  * @param {Object} config A config object containing the objects needed for the Store to access data,
22816  * and read the data into Records.
22817  */
22818 Roo.data.Store = function(config){
22819     this.data = new Roo.util.MixedCollection(false);
22820     this.data.getKey = function(o){
22821         return o.id;
22822     };
22823     this.baseParams = {};
22824     // private
22825     this.paramNames = {
22826         "start" : "start",
22827         "limit" : "limit",
22828         "sort" : "sort",
22829         "dir" : "dir",
22830         "multisort" : "_multisort"
22831     };
22832
22833     if(config && config.data){
22834         this.inlineData = config.data;
22835         delete config.data;
22836     }
22837
22838     Roo.apply(this, config);
22839     
22840     if(this.reader){ // reader passed
22841         this.reader = Roo.factory(this.reader, Roo.data);
22842         this.reader.xmodule = this.xmodule || false;
22843         if(!this.recordType){
22844             this.recordType = this.reader.recordType;
22845         }
22846         if(this.reader.onMetaChange){
22847             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22848         }
22849     }
22850
22851     if(this.recordType){
22852         this.fields = this.recordType.prototype.fields;
22853     }
22854     this.modified = [];
22855
22856     this.addEvents({
22857         /**
22858          * @event datachanged
22859          * Fires when the data cache has changed, and a widget which is using this Store
22860          * as a Record cache should refresh its view.
22861          * @param {Store} this
22862          */
22863         datachanged : true,
22864         /**
22865          * @event metachange
22866          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22867          * @param {Store} this
22868          * @param {Object} meta The JSON metadata
22869          */
22870         metachange : true,
22871         /**
22872          * @event add
22873          * Fires when Records have been added to the Store
22874          * @param {Store} this
22875          * @param {Roo.data.Record[]} records The array of Records added
22876          * @param {Number} index The index at which the record(s) were added
22877          */
22878         add : true,
22879         /**
22880          * @event remove
22881          * Fires when a Record has been removed from the Store
22882          * @param {Store} this
22883          * @param {Roo.data.Record} record The Record that was removed
22884          * @param {Number} index The index at which the record was removed
22885          */
22886         remove : true,
22887         /**
22888          * @event update
22889          * Fires when a Record has been updated
22890          * @param {Store} this
22891          * @param {Roo.data.Record} record The Record that was updated
22892          * @param {String} operation The update operation being performed.  Value may be one of:
22893          * <pre><code>
22894  Roo.data.Record.EDIT
22895  Roo.data.Record.REJECT
22896  Roo.data.Record.COMMIT
22897          * </code></pre>
22898          */
22899         update : true,
22900         /**
22901          * @event clear
22902          * Fires when the data cache has been cleared.
22903          * @param {Store} this
22904          */
22905         clear : true,
22906         /**
22907          * @event beforeload
22908          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22909          * the load action will be canceled.
22910          * @param {Store} this
22911          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22912          */
22913         beforeload : true,
22914         /**
22915          * @event beforeloadadd
22916          * Fires after a new set of Records has been loaded.
22917          * @param {Store} this
22918          * @param {Roo.data.Record[]} records The Records that were loaded
22919          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22920          */
22921         beforeloadadd : true,
22922         /**
22923          * @event load
22924          * Fires after a new set of Records has been loaded, before they are added to the store.
22925          * @param {Store} this
22926          * @param {Roo.data.Record[]} records The Records that were loaded
22927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22928          * @params {Object} return from reader
22929          */
22930         load : true,
22931         /**
22932          * @event loadexception
22933          * Fires if an exception occurs in the Proxy during loading.
22934          * Called with the signature of the Proxy's "loadexception" event.
22935          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22936          * 
22937          * @param {Proxy} 
22938          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22939          * @param {Object} load options 
22940          * @param {Object} jsonData from your request (normally this contains the Exception)
22941          */
22942         loadexception : true
22943     });
22944     
22945     if(this.proxy){
22946         this.proxy = Roo.factory(this.proxy, Roo.data);
22947         this.proxy.xmodule = this.xmodule || false;
22948         this.relayEvents(this.proxy,  ["loadexception"]);
22949     }
22950     this.sortToggle = {};
22951     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22952
22953     Roo.data.Store.superclass.constructor.call(this);
22954
22955     if(this.inlineData){
22956         this.loadData(this.inlineData);
22957         delete this.inlineData;
22958     }
22959 };
22960
22961 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22962      /**
22963     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22964     * without a remote query - used by combo/forms at present.
22965     */
22966     
22967     /**
22968     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22969     */
22970     /**
22971     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22972     */
22973     /**
22974     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22975     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22976     */
22977     /**
22978     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22979     * on any HTTP request
22980     */
22981     /**
22982     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22983     */
22984     /**
22985     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22986     */
22987     multiSort: false,
22988     /**
22989     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22990     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22991     */
22992     remoteSort : false,
22993
22994     /**
22995     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22996      * loaded or when a record is removed. (defaults to false).
22997     */
22998     pruneModifiedRecords : false,
22999
23000     // private
23001     lastOptions : null,
23002
23003     /**
23004      * Add Records to the Store and fires the add event.
23005      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23006      */
23007     add : function(records){
23008         records = [].concat(records);
23009         for(var i = 0, len = records.length; i < len; i++){
23010             records[i].join(this);
23011         }
23012         var index = this.data.length;
23013         this.data.addAll(records);
23014         this.fireEvent("add", this, records, index);
23015     },
23016
23017     /**
23018      * Remove a Record from the Store and fires the remove event.
23019      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23020      */
23021     remove : function(record){
23022         var index = this.data.indexOf(record);
23023         this.data.removeAt(index);
23024  
23025         if(this.pruneModifiedRecords){
23026             this.modified.remove(record);
23027         }
23028         this.fireEvent("remove", this, record, index);
23029     },
23030
23031     /**
23032      * Remove all Records from the Store and fires the clear event.
23033      */
23034     removeAll : function(){
23035         this.data.clear();
23036         if(this.pruneModifiedRecords){
23037             this.modified = [];
23038         }
23039         this.fireEvent("clear", this);
23040     },
23041
23042     /**
23043      * Inserts Records to the Store at the given index and fires the add event.
23044      * @param {Number} index The start index at which to insert the passed Records.
23045      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23046      */
23047     insert : function(index, records){
23048         records = [].concat(records);
23049         for(var i = 0, len = records.length; i < len; i++){
23050             this.data.insert(index, records[i]);
23051             records[i].join(this);
23052         }
23053         this.fireEvent("add", this, records, index);
23054     },
23055
23056     /**
23057      * Get the index within the cache of the passed Record.
23058      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23059      * @return {Number} The index of the passed Record. Returns -1 if not found.
23060      */
23061     indexOf : function(record){
23062         return this.data.indexOf(record);
23063     },
23064
23065     /**
23066      * Get the index within the cache of the Record with the passed id.
23067      * @param {String} id The id of the Record to find.
23068      * @return {Number} The index of the Record. Returns -1 if not found.
23069      */
23070     indexOfId : function(id){
23071         return this.data.indexOfKey(id);
23072     },
23073
23074     /**
23075      * Get the Record with the specified id.
23076      * @param {String} id The id of the Record to find.
23077      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23078      */
23079     getById : function(id){
23080         return this.data.key(id);
23081     },
23082
23083     /**
23084      * Get the Record at the specified index.
23085      * @param {Number} index The index of the Record to find.
23086      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23087      */
23088     getAt : function(index){
23089         return this.data.itemAt(index);
23090     },
23091
23092     /**
23093      * Returns a range of Records between specified indices.
23094      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23095      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23096      * @return {Roo.data.Record[]} An array of Records
23097      */
23098     getRange : function(start, end){
23099         return this.data.getRange(start, end);
23100     },
23101
23102     // private
23103     storeOptions : function(o){
23104         o = Roo.apply({}, o);
23105         delete o.callback;
23106         delete o.scope;
23107         this.lastOptions = o;
23108     },
23109
23110     /**
23111      * Loads the Record cache from the configured Proxy using the configured Reader.
23112      * <p>
23113      * If using remote paging, then the first load call must specify the <em>start</em>
23114      * and <em>limit</em> properties in the options.params property to establish the initial
23115      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23116      * <p>
23117      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23118      * and this call will return before the new data has been loaded. Perform any post-processing
23119      * in a callback function, or in a "load" event handler.</strong>
23120      * <p>
23121      * @param {Object} options An object containing properties which control loading options:<ul>
23122      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23123      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23124      * passed the following arguments:<ul>
23125      * <li>r : Roo.data.Record[]</li>
23126      * <li>options: Options object from the load call</li>
23127      * <li>success: Boolean success indicator</li></ul></li>
23128      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23129      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23130      * </ul>
23131      */
23132     load : function(options){
23133         options = options || {};
23134         if(this.fireEvent("beforeload", this, options) !== false){
23135             this.storeOptions(options);
23136             var p = Roo.apply(options.params || {}, this.baseParams);
23137             // if meta was not loaded from remote source.. try requesting it.
23138             if (!this.reader.metaFromRemote) {
23139                 p._requestMeta = 1;
23140             }
23141             if(this.sortInfo && this.remoteSort){
23142                 var pn = this.paramNames;
23143                 p[pn["sort"]] = this.sortInfo.field;
23144                 p[pn["dir"]] = this.sortInfo.direction;
23145             }
23146             if (this.multiSort) {
23147                 var pn = this.paramNames;
23148                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23149             }
23150             
23151             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23152         }
23153     },
23154
23155     /**
23156      * Reloads the Record cache from the configured Proxy using the configured Reader and
23157      * the options from the last load operation performed.
23158      * @param {Object} options (optional) An object containing properties which may override the options
23159      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23160      * the most recently used options are reused).
23161      */
23162     reload : function(options){
23163         this.load(Roo.applyIf(options||{}, this.lastOptions));
23164     },
23165
23166     // private
23167     // Called as a callback by the Reader during a load operation.
23168     loadRecords : function(o, options, success){
23169         if(!o || success === false){
23170             if(success !== false){
23171                 this.fireEvent("load", this, [], options, o);
23172             }
23173             if(options.callback){
23174                 options.callback.call(options.scope || this, [], options, false);
23175             }
23176             return;
23177         }
23178         // if data returned failure - throw an exception.
23179         if (o.success === false) {
23180             // show a message if no listener is registered.
23181             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23182                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23183             }
23184             // loadmask wil be hooked into this..
23185             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23186             return;
23187         }
23188         var r = o.records, t = o.totalRecords || r.length;
23189         
23190         this.fireEvent("beforeloadadd", this, r, options, o);
23191         
23192         if(!options || options.add !== true){
23193             if(this.pruneModifiedRecords){
23194                 this.modified = [];
23195             }
23196             for(var i = 0, len = r.length; i < len; i++){
23197                 r[i].join(this);
23198             }
23199             if(this.snapshot){
23200                 this.data = this.snapshot;
23201                 delete this.snapshot;
23202             }
23203             this.data.clear();
23204             this.data.addAll(r);
23205             this.totalLength = t;
23206             this.applySort();
23207             this.fireEvent("datachanged", this);
23208         }else{
23209             this.totalLength = Math.max(t, this.data.length+r.length);
23210             this.add(r);
23211         }
23212         
23213         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23214                 
23215             var e = new Roo.data.Record({});
23216
23217             e.set(this.parent.displayField, this.parent.emptyTitle);
23218             e.set(this.parent.valueField, '');
23219
23220             this.insert(0, e);
23221         }
23222             
23223         this.fireEvent("load", this, r, options, o);
23224         if(options.callback){
23225             options.callback.call(options.scope || this, r, options, true);
23226         }
23227     },
23228
23229
23230     /**
23231      * Loads data from a passed data block. A Reader which understands the format of the data
23232      * must have been configured in the constructor.
23233      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23234      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23235      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23236      */
23237     loadData : function(o, append){
23238         var r = this.reader.readRecords(o);
23239         this.loadRecords(r, {add: append}, true);
23240     },
23241
23242     /**
23243      * Gets the number of cached records.
23244      * <p>
23245      * <em>If using paging, this may not be the total size of the dataset. If the data object
23246      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23247      * the data set size</em>
23248      */
23249     getCount : function(){
23250         return this.data.length || 0;
23251     },
23252
23253     /**
23254      * Gets the total number of records in the dataset as returned by the server.
23255      * <p>
23256      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23257      * the dataset size</em>
23258      */
23259     getTotalCount : function(){
23260         return this.totalLength || 0;
23261     },
23262
23263     /**
23264      * Returns the sort state of the Store as an object with two properties:
23265      * <pre><code>
23266  field {String} The name of the field by which the Records are sorted
23267  direction {String} The sort order, "ASC" or "DESC"
23268      * </code></pre>
23269      */
23270     getSortState : function(){
23271         return this.sortInfo;
23272     },
23273
23274     // private
23275     applySort : function(){
23276         if(this.sortInfo && !this.remoteSort){
23277             var s = this.sortInfo, f = s.field;
23278             var st = this.fields.get(f).sortType;
23279             var fn = function(r1, r2){
23280                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23281                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23282             };
23283             this.data.sort(s.direction, fn);
23284             if(this.snapshot && this.snapshot != this.data){
23285                 this.snapshot.sort(s.direction, fn);
23286             }
23287         }
23288     },
23289
23290     /**
23291      * Sets the default sort column and order to be used by the next load operation.
23292      * @param {String} fieldName The name of the field to sort by.
23293      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23294      */
23295     setDefaultSort : function(field, dir){
23296         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23297     },
23298
23299     /**
23300      * Sort the Records.
23301      * If remote sorting is used, the sort is performed on the server, and the cache is
23302      * reloaded. If local sorting is used, the cache is sorted internally.
23303      * @param {String} fieldName The name of the field to sort by.
23304      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23305      */
23306     sort : function(fieldName, dir){
23307         var f = this.fields.get(fieldName);
23308         if(!dir){
23309             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23310             
23311             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23312                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23313             }else{
23314                 dir = f.sortDir;
23315             }
23316         }
23317         this.sortToggle[f.name] = dir;
23318         this.sortInfo = {field: f.name, direction: dir};
23319         if(!this.remoteSort){
23320             this.applySort();
23321             this.fireEvent("datachanged", this);
23322         }else{
23323             this.load(this.lastOptions);
23324         }
23325     },
23326
23327     /**
23328      * Calls the specified function for each of the Records in the cache.
23329      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23330      * Returning <em>false</em> aborts and exits the iteration.
23331      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23332      */
23333     each : function(fn, scope){
23334         this.data.each(fn, scope);
23335     },
23336
23337     /**
23338      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23339      * (e.g., during paging).
23340      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23341      */
23342     getModifiedRecords : function(){
23343         return this.modified;
23344     },
23345
23346     // private
23347     createFilterFn : function(property, value, anyMatch){
23348         if(!value.exec){ // not a regex
23349             value = String(value);
23350             if(value.length == 0){
23351                 return false;
23352             }
23353             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23354         }
23355         return function(r){
23356             return value.test(r.data[property]);
23357         };
23358     },
23359
23360     /**
23361      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23362      * @param {String} property A field on your records
23363      * @param {Number} start The record index to start at (defaults to 0)
23364      * @param {Number} end The last record index to include (defaults to length - 1)
23365      * @return {Number} The sum
23366      */
23367     sum : function(property, start, end){
23368         var rs = this.data.items, v = 0;
23369         start = start || 0;
23370         end = (end || end === 0) ? end : rs.length-1;
23371
23372         for(var i = start; i <= end; i++){
23373             v += (rs[i].data[property] || 0);
23374         }
23375         return v;
23376     },
23377
23378     /**
23379      * Filter the records by a specified property.
23380      * @param {String} field A field on your records
23381      * @param {String/RegExp} value Either a string that the field
23382      * should start with or a RegExp to test against the field
23383      * @param {Boolean} anyMatch True to match any part not just the beginning
23384      */
23385     filter : function(property, value, anyMatch){
23386         var fn = this.createFilterFn(property, value, anyMatch);
23387         return fn ? this.filterBy(fn) : this.clearFilter();
23388     },
23389
23390     /**
23391      * Filter by a function. The specified function will be called with each
23392      * record in this data source. If the function returns true the record is included,
23393      * otherwise it is filtered.
23394      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23395      * @param {Object} scope (optional) The scope of the function (defaults to this)
23396      */
23397     filterBy : function(fn, scope){
23398         this.snapshot = this.snapshot || this.data;
23399         this.data = this.queryBy(fn, scope||this);
23400         this.fireEvent("datachanged", this);
23401     },
23402
23403     /**
23404      * Query the records by a specified property.
23405      * @param {String} field A field on your records
23406      * @param {String/RegExp} value Either a string that the field
23407      * should start with or a RegExp to test against the field
23408      * @param {Boolean} anyMatch True to match any part not just the beginning
23409      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23410      */
23411     query : function(property, value, anyMatch){
23412         var fn = this.createFilterFn(property, value, anyMatch);
23413         return fn ? this.queryBy(fn) : this.data.clone();
23414     },
23415
23416     /**
23417      * Query by a function. The specified function will be called with each
23418      * record in this data source. If the function returns true the record is included
23419      * in the results.
23420      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23421      * @param {Object} scope (optional) The scope of the function (defaults to this)
23422       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23423      **/
23424     queryBy : function(fn, scope){
23425         var data = this.snapshot || this.data;
23426         return data.filterBy(fn, scope||this);
23427     },
23428
23429     /**
23430      * Collects unique values for a particular dataIndex from this store.
23431      * @param {String} dataIndex The property to collect
23432      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23433      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23434      * @return {Array} An array of the unique values
23435      **/
23436     collect : function(dataIndex, allowNull, bypassFilter){
23437         var d = (bypassFilter === true && this.snapshot) ?
23438                 this.snapshot.items : this.data.items;
23439         var v, sv, r = [], l = {};
23440         for(var i = 0, len = d.length; i < len; i++){
23441             v = d[i].data[dataIndex];
23442             sv = String(v);
23443             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23444                 l[sv] = true;
23445                 r[r.length] = v;
23446             }
23447         }
23448         return r;
23449     },
23450
23451     /**
23452      * Revert to a view of the Record cache with no filtering applied.
23453      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23454      */
23455     clearFilter : function(suppressEvent){
23456         if(this.snapshot && this.snapshot != this.data){
23457             this.data = this.snapshot;
23458             delete this.snapshot;
23459             if(suppressEvent !== true){
23460                 this.fireEvent("datachanged", this);
23461             }
23462         }
23463     },
23464
23465     // private
23466     afterEdit : function(record){
23467         if(this.modified.indexOf(record) == -1){
23468             this.modified.push(record);
23469         }
23470         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23471     },
23472     
23473     // private
23474     afterReject : function(record){
23475         this.modified.remove(record);
23476         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23477     },
23478
23479     // private
23480     afterCommit : function(record){
23481         this.modified.remove(record);
23482         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23483     },
23484
23485     /**
23486      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23487      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23488      */
23489     commitChanges : function(){
23490         var m = this.modified.slice(0);
23491         this.modified = [];
23492         for(var i = 0, len = m.length; i < len; i++){
23493             m[i].commit();
23494         }
23495     },
23496
23497     /**
23498      * Cancel outstanding changes on all changed records.
23499      */
23500     rejectChanges : function(){
23501         var m = this.modified.slice(0);
23502         this.modified = [];
23503         for(var i = 0, len = m.length; i < len; i++){
23504             m[i].reject();
23505         }
23506     },
23507
23508     onMetaChange : function(meta, rtype, o){
23509         this.recordType = rtype;
23510         this.fields = rtype.prototype.fields;
23511         delete this.snapshot;
23512         this.sortInfo = meta.sortInfo || this.sortInfo;
23513         this.modified = [];
23514         this.fireEvent('metachange', this, this.reader.meta);
23515     },
23516     
23517     moveIndex : function(data, type)
23518     {
23519         var index = this.indexOf(data);
23520         
23521         var newIndex = index + type;
23522         
23523         this.remove(data);
23524         
23525         this.insert(newIndex, data);
23526         
23527     }
23528 });/*
23529  * Based on:
23530  * Ext JS Library 1.1.1
23531  * Copyright(c) 2006-2007, Ext JS, LLC.
23532  *
23533  * Originally Released Under LGPL - original licence link has changed is not relivant.
23534  *
23535  * Fork - LGPL
23536  * <script type="text/javascript">
23537  */
23538
23539 /**
23540  * @class Roo.data.SimpleStore
23541  * @extends Roo.data.Store
23542  * Small helper class to make creating Stores from Array data easier.
23543  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23544  * @cfg {Array} fields An array of field definition objects, or field name strings.
23545  * @cfg {Object} an existing reader (eg. copied from another store)
23546  * @cfg {Array} data The multi-dimensional array of data
23547  * @constructor
23548  * @param {Object} config
23549  */
23550 Roo.data.SimpleStore = function(config)
23551 {
23552     Roo.data.SimpleStore.superclass.constructor.call(this, {
23553         isLocal : true,
23554         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23555                 id: config.id
23556             },
23557             Roo.data.Record.create(config.fields)
23558         ),
23559         proxy : new Roo.data.MemoryProxy(config.data)
23560     });
23561     this.load();
23562 };
23563 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23564  * Based on:
23565  * Ext JS Library 1.1.1
23566  * Copyright(c) 2006-2007, Ext JS, LLC.
23567  *
23568  * Originally Released Under LGPL - original licence link has changed is not relivant.
23569  *
23570  * Fork - LGPL
23571  * <script type="text/javascript">
23572  */
23573
23574 /**
23575 /**
23576  * @extends Roo.data.Store
23577  * @class Roo.data.JsonStore
23578  * Small helper class to make creating Stores for JSON data easier. <br/>
23579 <pre><code>
23580 var store = new Roo.data.JsonStore({
23581     url: 'get-images.php',
23582     root: 'images',
23583     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23584 });
23585 </code></pre>
23586  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23587  * JsonReader and HttpProxy (unless inline data is provided).</b>
23588  * @cfg {Array} fields An array of field definition objects, or field name strings.
23589  * @constructor
23590  * @param {Object} config
23591  */
23592 Roo.data.JsonStore = function(c){
23593     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23594         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23595         reader: new Roo.data.JsonReader(c, c.fields)
23596     }));
23597 };
23598 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23599  * Based on:
23600  * Ext JS Library 1.1.1
23601  * Copyright(c) 2006-2007, Ext JS, LLC.
23602  *
23603  * Originally Released Under LGPL - original licence link has changed is not relivant.
23604  *
23605  * Fork - LGPL
23606  * <script type="text/javascript">
23607  */
23608
23609  
23610 Roo.data.Field = function(config){
23611     if(typeof config == "string"){
23612         config = {name: config};
23613     }
23614     Roo.apply(this, config);
23615     
23616     if(!this.type){
23617         this.type = "auto";
23618     }
23619     
23620     var st = Roo.data.SortTypes;
23621     // named sortTypes are supported, here we look them up
23622     if(typeof this.sortType == "string"){
23623         this.sortType = st[this.sortType];
23624     }
23625     
23626     // set default sortType for strings and dates
23627     if(!this.sortType){
23628         switch(this.type){
23629             case "string":
23630                 this.sortType = st.asUCString;
23631                 break;
23632             case "date":
23633                 this.sortType = st.asDate;
23634                 break;
23635             default:
23636                 this.sortType = st.none;
23637         }
23638     }
23639
23640     // define once
23641     var stripRe = /[\$,%]/g;
23642
23643     // prebuilt conversion function for this field, instead of
23644     // switching every time we're reading a value
23645     if(!this.convert){
23646         var cv, dateFormat = this.dateFormat;
23647         switch(this.type){
23648             case "":
23649             case "auto":
23650             case undefined:
23651                 cv = function(v){ return v; };
23652                 break;
23653             case "string":
23654                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23655                 break;
23656             case "int":
23657                 cv = function(v){
23658                     return v !== undefined && v !== null && v !== '' ?
23659                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23660                     };
23661                 break;
23662             case "float":
23663                 cv = function(v){
23664                     return v !== undefined && v !== null && v !== '' ?
23665                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23666                     };
23667                 break;
23668             case "bool":
23669             case "boolean":
23670                 cv = function(v){ return v === true || v === "true" || v == 1; };
23671                 break;
23672             case "date":
23673                 cv = function(v){
23674                     if(!v){
23675                         return '';
23676                     }
23677                     if(v instanceof Date){
23678                         return v;
23679                     }
23680                     if(dateFormat){
23681                         if(dateFormat == "timestamp"){
23682                             return new Date(v*1000);
23683                         }
23684                         return Date.parseDate(v, dateFormat);
23685                     }
23686                     var parsed = Date.parse(v);
23687                     return parsed ? new Date(parsed) : null;
23688                 };
23689              break;
23690             
23691         }
23692         this.convert = cv;
23693     }
23694 };
23695
23696 Roo.data.Field.prototype = {
23697     dateFormat: null,
23698     defaultValue: "",
23699     mapping: null,
23700     sortType : null,
23701     sortDir : "ASC"
23702 };/*
23703  * Based on:
23704  * Ext JS Library 1.1.1
23705  * Copyright(c) 2006-2007, Ext JS, LLC.
23706  *
23707  * Originally Released Under LGPL - original licence link has changed is not relivant.
23708  *
23709  * Fork - LGPL
23710  * <script type="text/javascript">
23711  */
23712  
23713 // Base class for reading structured data from a data source.  This class is intended to be
23714 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23715
23716 /**
23717  * @class Roo.data.DataReader
23718  * Base class for reading structured data from a data source.  This class is intended to be
23719  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23720  */
23721
23722 Roo.data.DataReader = function(meta, recordType){
23723     
23724     this.meta = meta;
23725     
23726     this.recordType = recordType instanceof Array ? 
23727         Roo.data.Record.create(recordType) : recordType;
23728 };
23729
23730 Roo.data.DataReader.prototype = {
23731      /**
23732      * Create an empty record
23733      * @param {Object} data (optional) - overlay some values
23734      * @return {Roo.data.Record} record created.
23735      */
23736     newRow :  function(d) {
23737         var da =  {};
23738         this.recordType.prototype.fields.each(function(c) {
23739             switch( c.type) {
23740                 case 'int' : da[c.name] = 0; break;
23741                 case 'date' : da[c.name] = new Date(); break;
23742                 case 'float' : da[c.name] = 0.0; break;
23743                 case 'boolean' : da[c.name] = false; break;
23744                 default : da[c.name] = ""; break;
23745             }
23746             
23747         });
23748         return new this.recordType(Roo.apply(da, d));
23749     }
23750     
23751 };/*
23752  * Based on:
23753  * Ext JS Library 1.1.1
23754  * Copyright(c) 2006-2007, Ext JS, LLC.
23755  *
23756  * Originally Released Under LGPL - original licence link has changed is not relivant.
23757  *
23758  * Fork - LGPL
23759  * <script type="text/javascript">
23760  */
23761
23762 /**
23763  * @class Roo.data.DataProxy
23764  * @extends Roo.data.Observable
23765  * This class is an abstract base class for implementations which provide retrieval of
23766  * unformatted data objects.<br>
23767  * <p>
23768  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23769  * (of the appropriate type which knows how to parse the data object) to provide a block of
23770  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23771  * <p>
23772  * Custom implementations must implement the load method as described in
23773  * {@link Roo.data.HttpProxy#load}.
23774  */
23775 Roo.data.DataProxy = function(){
23776     this.addEvents({
23777         /**
23778          * @event beforeload
23779          * Fires before a network request is made to retrieve a data object.
23780          * @param {Object} This DataProxy object.
23781          * @param {Object} params The params parameter to the load function.
23782          */
23783         beforeload : true,
23784         /**
23785          * @event load
23786          * Fires before the load method's callback is called.
23787          * @param {Object} This DataProxy object.
23788          * @param {Object} o The data object.
23789          * @param {Object} arg The callback argument object passed to the load function.
23790          */
23791         load : true,
23792         /**
23793          * @event loadexception
23794          * Fires if an Exception occurs during data retrieval.
23795          * @param {Object} This DataProxy object.
23796          * @param {Object} o The data object.
23797          * @param {Object} arg The callback argument object passed to the load function.
23798          * @param {Object} e The Exception.
23799          */
23800         loadexception : true
23801     });
23802     Roo.data.DataProxy.superclass.constructor.call(this);
23803 };
23804
23805 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23806
23807     /**
23808      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23809      */
23810 /*
23811  * Based on:
23812  * Ext JS Library 1.1.1
23813  * Copyright(c) 2006-2007, Ext JS, LLC.
23814  *
23815  * Originally Released Under LGPL - original licence link has changed is not relivant.
23816  *
23817  * Fork - LGPL
23818  * <script type="text/javascript">
23819  */
23820 /**
23821  * @class Roo.data.MemoryProxy
23822  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23823  * to the Reader when its load method is called.
23824  * @constructor
23825  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23826  */
23827 Roo.data.MemoryProxy = function(data){
23828     if (data.data) {
23829         data = data.data;
23830     }
23831     Roo.data.MemoryProxy.superclass.constructor.call(this);
23832     this.data = data;
23833 };
23834
23835 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23836     
23837     /**
23838      * Load data from the requested source (in this case an in-memory
23839      * data object passed to the constructor), read the data object into
23840      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23841      * process that block using the passed callback.
23842      * @param {Object} params This parameter is not used by the MemoryProxy class.
23843      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23844      * object into a block of Roo.data.Records.
23845      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23846      * The function must be passed <ul>
23847      * <li>The Record block object</li>
23848      * <li>The "arg" argument from the load function</li>
23849      * <li>A boolean success indicator</li>
23850      * </ul>
23851      * @param {Object} scope The scope in which to call the callback
23852      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23853      */
23854     load : function(params, reader, callback, scope, arg){
23855         params = params || {};
23856         var result;
23857         try {
23858             result = reader.readRecords(params.data ? params.data :this.data);
23859         }catch(e){
23860             this.fireEvent("loadexception", this, arg, null, e);
23861             callback.call(scope, null, arg, false);
23862             return;
23863         }
23864         callback.call(scope, result, arg, true);
23865     },
23866     
23867     // private
23868     update : function(params, records){
23869         
23870     }
23871 });/*
23872  * Based on:
23873  * Ext JS Library 1.1.1
23874  * Copyright(c) 2006-2007, Ext JS, LLC.
23875  *
23876  * Originally Released Under LGPL - original licence link has changed is not relivant.
23877  *
23878  * Fork - LGPL
23879  * <script type="text/javascript">
23880  */
23881 /**
23882  * @class Roo.data.HttpProxy
23883  * @extends Roo.data.DataProxy
23884  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23885  * configured to reference a certain URL.<br><br>
23886  * <p>
23887  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23888  * from which the running page was served.<br><br>
23889  * <p>
23890  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23891  * <p>
23892  * Be aware that to enable the browser to parse an XML document, the server must set
23893  * the Content-Type header in the HTTP response to "text/xml".
23894  * @constructor
23895  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23896  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23897  * will be used to make the request.
23898  */
23899 Roo.data.HttpProxy = function(conn){
23900     Roo.data.HttpProxy.superclass.constructor.call(this);
23901     // is conn a conn config or a real conn?
23902     this.conn = conn;
23903     this.useAjax = !conn || !conn.events;
23904   
23905 };
23906
23907 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23908     // thse are take from connection...
23909     
23910     /**
23911      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23912      */
23913     /**
23914      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23915      * extra parameters to each request made by this object. (defaults to undefined)
23916      */
23917     /**
23918      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23919      *  to each request made by this object. (defaults to undefined)
23920      */
23921     /**
23922      * @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)
23923      */
23924     /**
23925      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23926      */
23927      /**
23928      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23929      * @type Boolean
23930      */
23931   
23932
23933     /**
23934      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23935      * @type Boolean
23936      */
23937     /**
23938      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23939      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23940      * a finer-grained basis than the DataProxy events.
23941      */
23942     getConnection : function(){
23943         return this.useAjax ? Roo.Ajax : this.conn;
23944     },
23945
23946     /**
23947      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23948      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23949      * process that block using the passed callback.
23950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23951      * for the request to the remote server.
23952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23953      * object into a block of Roo.data.Records.
23954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23955      * The function must be passed <ul>
23956      * <li>The Record block object</li>
23957      * <li>The "arg" argument from the load function</li>
23958      * <li>A boolean success indicator</li>
23959      * </ul>
23960      * @param {Object} scope The scope in which to call the callback
23961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23962      */
23963     load : function(params, reader, callback, scope, arg){
23964         if(this.fireEvent("beforeload", this, params) !== false){
23965             var  o = {
23966                 params : params || {},
23967                 request: {
23968                     callback : callback,
23969                     scope : scope,
23970                     arg : arg
23971                 },
23972                 reader: reader,
23973                 callback : this.loadResponse,
23974                 scope: this
23975             };
23976             if(this.useAjax){
23977                 Roo.applyIf(o, this.conn);
23978                 if(this.activeRequest){
23979                     Roo.Ajax.abort(this.activeRequest);
23980                 }
23981                 this.activeRequest = Roo.Ajax.request(o);
23982             }else{
23983                 this.conn.request(o);
23984             }
23985         }else{
23986             callback.call(scope||this, null, arg, false);
23987         }
23988     },
23989
23990     // private
23991     loadResponse : function(o, success, response){
23992         delete this.activeRequest;
23993         if(!success){
23994             this.fireEvent("loadexception", this, o, response);
23995             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23996             return;
23997         }
23998         var result;
23999         try {
24000             result = o.reader.read(response);
24001         }catch(e){
24002             this.fireEvent("loadexception", this, o, response, e);
24003             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24004             return;
24005         }
24006         
24007         this.fireEvent("load", this, o, o.request.arg);
24008         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24009     },
24010
24011     // private
24012     update : function(dataSet){
24013
24014     },
24015
24016     // private
24017     updateResponse : function(dataSet){
24018
24019     }
24020 });/*
24021  * Based on:
24022  * Ext JS Library 1.1.1
24023  * Copyright(c) 2006-2007, Ext JS, LLC.
24024  *
24025  * Originally Released Under LGPL - original licence link has changed is not relivant.
24026  *
24027  * Fork - LGPL
24028  * <script type="text/javascript">
24029  */
24030
24031 /**
24032  * @class Roo.data.ScriptTagProxy
24033  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24034  * other than the originating domain of the running page.<br><br>
24035  * <p>
24036  * <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
24037  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24038  * <p>
24039  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24040  * source code that is used as the source inside a &lt;script> tag.<br><br>
24041  * <p>
24042  * In order for the browser to process the returned data, the server must wrap the data object
24043  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24044  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24045  * depending on whether the callback name was passed:
24046  * <p>
24047  * <pre><code>
24048 boolean scriptTag = false;
24049 String cb = request.getParameter("callback");
24050 if (cb != null) {
24051     scriptTag = true;
24052     response.setContentType("text/javascript");
24053 } else {
24054     response.setContentType("application/x-json");
24055 }
24056 Writer out = response.getWriter();
24057 if (scriptTag) {
24058     out.write(cb + "(");
24059 }
24060 out.print(dataBlock.toJsonString());
24061 if (scriptTag) {
24062     out.write(");");
24063 }
24064 </pre></code>
24065  *
24066  * @constructor
24067  * @param {Object} config A configuration object.
24068  */
24069 Roo.data.ScriptTagProxy = function(config){
24070     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24071     Roo.apply(this, config);
24072     this.head = document.getElementsByTagName("head")[0];
24073 };
24074
24075 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24076
24077 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24078     /**
24079      * @cfg {String} url The URL from which to request the data object.
24080      */
24081     /**
24082      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24083      */
24084     timeout : 30000,
24085     /**
24086      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24087      * the server the name of the callback function set up by the load call to process the returned data object.
24088      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24089      * javascript output which calls this named function passing the data object as its only parameter.
24090      */
24091     callbackParam : "callback",
24092     /**
24093      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24094      * name to the request.
24095      */
24096     nocache : true,
24097
24098     /**
24099      * Load data from the configured URL, read the data object into
24100      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24101      * process that block using the passed callback.
24102      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24103      * for the request to the remote server.
24104      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24105      * object into a block of Roo.data.Records.
24106      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24107      * The function must be passed <ul>
24108      * <li>The Record block object</li>
24109      * <li>The "arg" argument from the load function</li>
24110      * <li>A boolean success indicator</li>
24111      * </ul>
24112      * @param {Object} scope The scope in which to call the callback
24113      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24114      */
24115     load : function(params, reader, callback, scope, arg){
24116         if(this.fireEvent("beforeload", this, params) !== false){
24117
24118             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24119
24120             var url = this.url;
24121             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24122             if(this.nocache){
24123                 url += "&_dc=" + (new Date().getTime());
24124             }
24125             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24126             var trans = {
24127                 id : transId,
24128                 cb : "stcCallback"+transId,
24129                 scriptId : "stcScript"+transId,
24130                 params : params,
24131                 arg : arg,
24132                 url : url,
24133                 callback : callback,
24134                 scope : scope,
24135                 reader : reader
24136             };
24137             var conn = this;
24138
24139             window[trans.cb] = function(o){
24140                 conn.handleResponse(o, trans);
24141             };
24142
24143             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24144
24145             if(this.autoAbort !== false){
24146                 this.abort();
24147             }
24148
24149             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24150
24151             var script = document.createElement("script");
24152             script.setAttribute("src", url);
24153             script.setAttribute("type", "text/javascript");
24154             script.setAttribute("id", trans.scriptId);
24155             this.head.appendChild(script);
24156
24157             this.trans = trans;
24158         }else{
24159             callback.call(scope||this, null, arg, false);
24160         }
24161     },
24162
24163     // private
24164     isLoading : function(){
24165         return this.trans ? true : false;
24166     },
24167
24168     /**
24169      * Abort the current server request.
24170      */
24171     abort : function(){
24172         if(this.isLoading()){
24173             this.destroyTrans(this.trans);
24174         }
24175     },
24176
24177     // private
24178     destroyTrans : function(trans, isLoaded){
24179         this.head.removeChild(document.getElementById(trans.scriptId));
24180         clearTimeout(trans.timeoutId);
24181         if(isLoaded){
24182             window[trans.cb] = undefined;
24183             try{
24184                 delete window[trans.cb];
24185             }catch(e){}
24186         }else{
24187             // if hasn't been loaded, wait for load to remove it to prevent script error
24188             window[trans.cb] = function(){
24189                 window[trans.cb] = undefined;
24190                 try{
24191                     delete window[trans.cb];
24192                 }catch(e){}
24193             };
24194         }
24195     },
24196
24197     // private
24198     handleResponse : function(o, trans){
24199         this.trans = false;
24200         this.destroyTrans(trans, true);
24201         var result;
24202         try {
24203             result = trans.reader.readRecords(o);
24204         }catch(e){
24205             this.fireEvent("loadexception", this, o, trans.arg, e);
24206             trans.callback.call(trans.scope||window, null, trans.arg, false);
24207             return;
24208         }
24209         this.fireEvent("load", this, o, trans.arg);
24210         trans.callback.call(trans.scope||window, result, trans.arg, true);
24211     },
24212
24213     // private
24214     handleFailure : function(trans){
24215         this.trans = false;
24216         this.destroyTrans(trans, false);
24217         this.fireEvent("loadexception", this, null, trans.arg);
24218         trans.callback.call(trans.scope||window, null, trans.arg, false);
24219     }
24220 });/*
24221  * Based on:
24222  * Ext JS Library 1.1.1
24223  * Copyright(c) 2006-2007, Ext JS, LLC.
24224  *
24225  * Originally Released Under LGPL - original licence link has changed is not relivant.
24226  *
24227  * Fork - LGPL
24228  * <script type="text/javascript">
24229  */
24230
24231 /**
24232  * @class Roo.data.JsonReader
24233  * @extends Roo.data.DataReader
24234  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24235  * based on mappings in a provided Roo.data.Record constructor.
24236  * 
24237  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24238  * in the reply previously. 
24239  * 
24240  * <p>
24241  * Example code:
24242  * <pre><code>
24243 var RecordDef = Roo.data.Record.create([
24244     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24245     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24246 ]);
24247 var myReader = new Roo.data.JsonReader({
24248     totalProperty: "results",    // The property which contains the total dataset size (optional)
24249     root: "rows",                // The property which contains an Array of row objects
24250     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24251 }, RecordDef);
24252 </code></pre>
24253  * <p>
24254  * This would consume a JSON file like this:
24255  * <pre><code>
24256 { 'results': 2, 'rows': [
24257     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24258     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24259 }
24260 </code></pre>
24261  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24262  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24263  * paged from the remote server.
24264  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24265  * @cfg {String} root name of the property which contains the Array of row objects.
24266  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24267  * @cfg {Array} fields Array of field definition objects
24268  * @constructor
24269  * Create a new JsonReader
24270  * @param {Object} meta Metadata configuration options
24271  * @param {Object} recordType Either an Array of field definition objects,
24272  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24273  */
24274 Roo.data.JsonReader = function(meta, recordType){
24275     
24276     meta = meta || {};
24277     // set some defaults:
24278     Roo.applyIf(meta, {
24279         totalProperty: 'total',
24280         successProperty : 'success',
24281         root : 'data',
24282         id : 'id'
24283     });
24284     
24285     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24286 };
24287 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24288     
24289     /**
24290      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24291      * Used by Store query builder to append _requestMeta to params.
24292      * 
24293      */
24294     metaFromRemote : false,
24295     /**
24296      * This method is only used by a DataProxy which has retrieved data from a remote server.
24297      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24298      * @return {Object} data A data block which is used by an Roo.data.Store object as
24299      * a cache of Roo.data.Records.
24300      */
24301     read : function(response){
24302         var json = response.responseText;
24303        
24304         var o = /* eval:var:o */ eval("("+json+")");
24305         if(!o) {
24306             throw {message: "JsonReader.read: Json object not found"};
24307         }
24308         
24309         if(o.metaData){
24310             
24311             delete this.ef;
24312             this.metaFromRemote = true;
24313             this.meta = o.metaData;
24314             this.recordType = Roo.data.Record.create(o.metaData.fields);
24315             this.onMetaChange(this.meta, this.recordType, o);
24316         }
24317         return this.readRecords(o);
24318     },
24319
24320     // private function a store will implement
24321     onMetaChange : function(meta, recordType, o){
24322
24323     },
24324
24325     /**
24326          * @ignore
24327          */
24328     simpleAccess: function(obj, subsc) {
24329         return obj[subsc];
24330     },
24331
24332         /**
24333          * @ignore
24334          */
24335     getJsonAccessor: function(){
24336         var re = /[\[\.]/;
24337         return function(expr) {
24338             try {
24339                 return(re.test(expr))
24340                     ? new Function("obj", "return obj." + expr)
24341                     : function(obj){
24342                         return obj[expr];
24343                     };
24344             } catch(e){}
24345             return Roo.emptyFn;
24346         };
24347     }(),
24348
24349     /**
24350      * Create a data block containing Roo.data.Records from an XML document.
24351      * @param {Object} o An object which contains an Array of row objects in the property specified
24352      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24353      * which contains the total size of the dataset.
24354      * @return {Object} data A data block which is used by an Roo.data.Store object as
24355      * a cache of Roo.data.Records.
24356      */
24357     readRecords : function(o){
24358         /**
24359          * After any data loads, the raw JSON data is available for further custom processing.
24360          * @type Object
24361          */
24362         this.o = o;
24363         var s = this.meta, Record = this.recordType,
24364             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24365
24366 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24367         if (!this.ef) {
24368             if(s.totalProperty) {
24369                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24370                 }
24371                 if(s.successProperty) {
24372                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24373                 }
24374                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24375                 if (s.id) {
24376                         var g = this.getJsonAccessor(s.id);
24377                         this.getId = function(rec) {
24378                                 var r = g(rec);  
24379                                 return (r === undefined || r === "") ? null : r;
24380                         };
24381                 } else {
24382                         this.getId = function(){return null;};
24383                 }
24384             this.ef = [];
24385             for(var jj = 0; jj < fl; jj++){
24386                 f = fi[jj];
24387                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24388                 this.ef[jj] = this.getJsonAccessor(map);
24389             }
24390         }
24391
24392         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24393         if(s.totalProperty){
24394             var vt = parseInt(this.getTotal(o), 10);
24395             if(!isNaN(vt)){
24396                 totalRecords = vt;
24397             }
24398         }
24399         if(s.successProperty){
24400             var vs = this.getSuccess(o);
24401             if(vs === false || vs === 'false'){
24402                 success = false;
24403             }
24404         }
24405         var records = [];
24406         for(var i = 0; i < c; i++){
24407                 var n = root[i];
24408             var values = {};
24409             var id = this.getId(n);
24410             for(var j = 0; j < fl; j++){
24411                 f = fi[j];
24412             var v = this.ef[j](n);
24413             if (!f.convert) {
24414                 Roo.log('missing convert for ' + f.name);
24415                 Roo.log(f);
24416                 continue;
24417             }
24418             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24419             }
24420             var record = new Record(values, id);
24421             record.json = n;
24422             records[i] = record;
24423         }
24424         return {
24425             raw : o,
24426             success : success,
24427             records : records,
24428             totalRecords : totalRecords
24429         };
24430     }
24431 });/*
24432  * Based on:
24433  * Ext JS Library 1.1.1
24434  * Copyright(c) 2006-2007, Ext JS, LLC.
24435  *
24436  * Originally Released Under LGPL - original licence link has changed is not relivant.
24437  *
24438  * Fork - LGPL
24439  * <script type="text/javascript">
24440  */
24441
24442 /**
24443  * @class Roo.data.XmlReader
24444  * @extends Roo.data.DataReader
24445  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24446  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24447  * <p>
24448  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24449  * header in the HTTP response must be set to "text/xml".</em>
24450  * <p>
24451  * Example code:
24452  * <pre><code>
24453 var RecordDef = Roo.data.Record.create([
24454    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24455    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24456 ]);
24457 var myReader = new Roo.data.XmlReader({
24458    totalRecords: "results", // The element which contains the total dataset size (optional)
24459    record: "row",           // The repeated element which contains row information
24460    id: "id"                 // The element within the row that provides an ID for the record (optional)
24461 }, RecordDef);
24462 </code></pre>
24463  * <p>
24464  * This would consume an XML file like this:
24465  * <pre><code>
24466 &lt;?xml?>
24467 &lt;dataset>
24468  &lt;results>2&lt;/results>
24469  &lt;row>
24470    &lt;id>1&lt;/id>
24471    &lt;name>Bill&lt;/name>
24472    &lt;occupation>Gardener&lt;/occupation>
24473  &lt;/row>
24474  &lt;row>
24475    &lt;id>2&lt;/id>
24476    &lt;name>Ben&lt;/name>
24477    &lt;occupation>Horticulturalist&lt;/occupation>
24478  &lt;/row>
24479 &lt;/dataset>
24480 </code></pre>
24481  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24482  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24483  * paged from the remote server.
24484  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24485  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24486  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24487  * a record identifier value.
24488  * @constructor
24489  * Create a new XmlReader
24490  * @param {Object} meta Metadata configuration options
24491  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24492  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24493  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24494  */
24495 Roo.data.XmlReader = function(meta, recordType){
24496     meta = meta || {};
24497     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24498 };
24499 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24500     /**
24501      * This method is only used by a DataProxy which has retrieved data from a remote server.
24502          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24503          * to contain a method called 'responseXML' that returns an XML document object.
24504      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24505      * a cache of Roo.data.Records.
24506      */
24507     read : function(response){
24508         var doc = response.responseXML;
24509         if(!doc) {
24510             throw {message: "XmlReader.read: XML Document not available"};
24511         }
24512         return this.readRecords(doc);
24513     },
24514
24515     /**
24516      * Create a data block containing Roo.data.Records from an XML document.
24517          * @param {Object} doc A parsed XML document.
24518      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24519      * a cache of Roo.data.Records.
24520      */
24521     readRecords : function(doc){
24522         /**
24523          * After any data loads/reads, the raw XML Document is available for further custom processing.
24524          * @type XMLDocument
24525          */
24526         this.xmlData = doc;
24527         var root = doc.documentElement || doc;
24528         var q = Roo.DomQuery;
24529         var recordType = this.recordType, fields = recordType.prototype.fields;
24530         var sid = this.meta.id;
24531         var totalRecords = 0, success = true;
24532         if(this.meta.totalRecords){
24533             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24534         }
24535         
24536         if(this.meta.success){
24537             var sv = q.selectValue(this.meta.success, root, true);
24538             success = sv !== false && sv !== 'false';
24539         }
24540         var records = [];
24541         var ns = q.select(this.meta.record, root);
24542         for(var i = 0, len = ns.length; i < len; i++) {
24543                 var n = ns[i];
24544                 var values = {};
24545                 var id = sid ? q.selectValue(sid, n) : undefined;
24546                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24547                     var f = fields.items[j];
24548                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24549                     v = f.convert(v);
24550                     values[f.name] = v;
24551                 }
24552                 var record = new recordType(values, id);
24553                 record.node = n;
24554                 records[records.length] = record;
24555             }
24556
24557             return {
24558                 success : success,
24559                 records : records,
24560                 totalRecords : totalRecords || records.length
24561             };
24562     }
24563 });/*
24564  * Based on:
24565  * Ext JS Library 1.1.1
24566  * Copyright(c) 2006-2007, Ext JS, LLC.
24567  *
24568  * Originally Released Under LGPL - original licence link has changed is not relivant.
24569  *
24570  * Fork - LGPL
24571  * <script type="text/javascript">
24572  */
24573
24574 /**
24575  * @class Roo.data.ArrayReader
24576  * @extends Roo.data.DataReader
24577  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24578  * Each element of that Array represents a row of data fields. The
24579  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24580  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24581  * <p>
24582  * Example code:.
24583  * <pre><code>
24584 var RecordDef = Roo.data.Record.create([
24585     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24586     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24587 ]);
24588 var myReader = new Roo.data.ArrayReader({
24589     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24590 }, RecordDef);
24591 </code></pre>
24592  * <p>
24593  * This would consume an Array like this:
24594  * <pre><code>
24595 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24596   </code></pre>
24597  
24598  * @constructor
24599  * Create a new JsonReader
24600  * @param {Object} meta Metadata configuration options.
24601  * @param {Object|Array} recordType Either an Array of field definition objects
24602  * 
24603  * @cfg {Array} fields Array of field definition objects
24604  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24605  * as specified to {@link Roo.data.Record#create},
24606  * or an {@link Roo.data.Record} object
24607  *
24608  * 
24609  * created using {@link Roo.data.Record#create}.
24610  */
24611 Roo.data.ArrayReader = function(meta, recordType){
24612     
24613      
24614     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24615 };
24616
24617 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24618     /**
24619      * Create a data block containing Roo.data.Records from an XML document.
24620      * @param {Object} o An Array of row objects which represents the dataset.
24621      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24622      * a cache of Roo.data.Records.
24623      */
24624     readRecords : function(o)
24625     {
24626         var sid = this.meta ? this.meta.id : null;
24627         var recordType = this.recordType, fields = recordType.prototype.fields;
24628         var records = [];
24629         var root = o;
24630         for(var i = 0; i < root.length; i++){
24631                 var n = root[i];
24632             var values = {};
24633             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24634             for(var j = 0, jlen = fields.length; j < jlen; j++){
24635                 var f = fields.items[j];
24636                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24637                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24638                 v = f.convert(v);
24639                 values[f.name] = v;
24640             }
24641             var record = new recordType(values, id);
24642             record.json = n;
24643             records[records.length] = record;
24644         }
24645         return {
24646             records : records,
24647             totalRecords : records.length
24648         };
24649     }
24650 });/*
24651  * Based on:
24652  * Ext JS Library 1.1.1
24653  * Copyright(c) 2006-2007, Ext JS, LLC.
24654  *
24655  * Originally Released Under LGPL - original licence link has changed is not relivant.
24656  *
24657  * Fork - LGPL
24658  * <script type="text/javascript">
24659  */
24660
24661
24662 /**
24663  * @class Roo.data.Tree
24664  * @extends Roo.util.Observable
24665  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24666  * in the tree have most standard DOM functionality.
24667  * @constructor
24668  * @param {Node} root (optional) The root node
24669  */
24670 Roo.data.Tree = function(root){
24671    this.nodeHash = {};
24672    /**
24673     * The root node for this tree
24674     * @type Node
24675     */
24676    this.root = null;
24677    if(root){
24678        this.setRootNode(root);
24679    }
24680    this.addEvents({
24681        /**
24682         * @event append
24683         * Fires when a new child node is appended to a node in this tree.
24684         * @param {Tree} tree The owner tree
24685         * @param {Node} parent The parent node
24686         * @param {Node} node The newly appended node
24687         * @param {Number} index The index of the newly appended node
24688         */
24689        "append" : true,
24690        /**
24691         * @event remove
24692         * Fires when a child node is removed from a node in this tree.
24693         * @param {Tree} tree The owner tree
24694         * @param {Node} parent The parent node
24695         * @param {Node} node The child node removed
24696         */
24697        "remove" : true,
24698        /**
24699         * @event move
24700         * Fires when a node is moved to a new location in the tree
24701         * @param {Tree} tree The owner tree
24702         * @param {Node} node The node moved
24703         * @param {Node} oldParent The old parent of this node
24704         * @param {Node} newParent The new parent of this node
24705         * @param {Number} index The index it was moved to
24706         */
24707        "move" : true,
24708        /**
24709         * @event insert
24710         * Fires when a new child node is inserted in a node in this tree.
24711         * @param {Tree} tree The owner tree
24712         * @param {Node} parent The parent node
24713         * @param {Node} node The child node inserted
24714         * @param {Node} refNode The child node the node was inserted before
24715         */
24716        "insert" : true,
24717        /**
24718         * @event beforeappend
24719         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24720         * @param {Tree} tree The owner tree
24721         * @param {Node} parent The parent node
24722         * @param {Node} node The child node to be appended
24723         */
24724        "beforeappend" : true,
24725        /**
24726         * @event beforeremove
24727         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24728         * @param {Tree} tree The owner tree
24729         * @param {Node} parent The parent node
24730         * @param {Node} node The child node to be removed
24731         */
24732        "beforeremove" : true,
24733        /**
24734         * @event beforemove
24735         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24736         * @param {Tree} tree The owner tree
24737         * @param {Node} node The node being moved
24738         * @param {Node} oldParent The parent of the node
24739         * @param {Node} newParent The new parent the node is moving to
24740         * @param {Number} index The index it is being moved to
24741         */
24742        "beforemove" : true,
24743        /**
24744         * @event beforeinsert
24745         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24746         * @param {Tree} tree The owner tree
24747         * @param {Node} parent The parent node
24748         * @param {Node} node The child node to be inserted
24749         * @param {Node} refNode The child node the node is being inserted before
24750         */
24751        "beforeinsert" : true
24752    });
24753
24754     Roo.data.Tree.superclass.constructor.call(this);
24755 };
24756
24757 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24758     pathSeparator: "/",
24759
24760     proxyNodeEvent : function(){
24761         return this.fireEvent.apply(this, arguments);
24762     },
24763
24764     /**
24765      * Returns the root node for this tree.
24766      * @return {Node}
24767      */
24768     getRootNode : function(){
24769         return this.root;
24770     },
24771
24772     /**
24773      * Sets the root node for this tree.
24774      * @param {Node} node
24775      * @return {Node}
24776      */
24777     setRootNode : function(node){
24778         this.root = node;
24779         node.ownerTree = this;
24780         node.isRoot = true;
24781         this.registerNode(node);
24782         return node;
24783     },
24784
24785     /**
24786      * Gets a node in this tree by its id.
24787      * @param {String} id
24788      * @return {Node}
24789      */
24790     getNodeById : function(id){
24791         return this.nodeHash[id];
24792     },
24793
24794     registerNode : function(node){
24795         this.nodeHash[node.id] = node;
24796     },
24797
24798     unregisterNode : function(node){
24799         delete this.nodeHash[node.id];
24800     },
24801
24802     toString : function(){
24803         return "[Tree"+(this.id?" "+this.id:"")+"]";
24804     }
24805 });
24806
24807 /**
24808  * @class Roo.data.Node
24809  * @extends Roo.util.Observable
24810  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24811  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24812  * @constructor
24813  * @param {Object} attributes The attributes/config for the node
24814  */
24815 Roo.data.Node = function(attributes){
24816     /**
24817      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24818      * @type {Object}
24819      */
24820     this.attributes = attributes || {};
24821     this.leaf = this.attributes.leaf;
24822     /**
24823      * The node id. @type String
24824      */
24825     this.id = this.attributes.id;
24826     if(!this.id){
24827         this.id = Roo.id(null, "ynode-");
24828         this.attributes.id = this.id;
24829     }
24830      
24831     
24832     /**
24833      * All child nodes of this node. @type Array
24834      */
24835     this.childNodes = [];
24836     if(!this.childNodes.indexOf){ // indexOf is a must
24837         this.childNodes.indexOf = function(o){
24838             for(var i = 0, len = this.length; i < len; i++){
24839                 if(this[i] == o) {
24840                     return i;
24841                 }
24842             }
24843             return -1;
24844         };
24845     }
24846     /**
24847      * The parent node for this node. @type Node
24848      */
24849     this.parentNode = null;
24850     /**
24851      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24852      */
24853     this.firstChild = null;
24854     /**
24855      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24856      */
24857     this.lastChild = null;
24858     /**
24859      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24860      */
24861     this.previousSibling = null;
24862     /**
24863      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24864      */
24865     this.nextSibling = null;
24866
24867     this.addEvents({
24868        /**
24869         * @event append
24870         * Fires when a new child node is appended
24871         * @param {Tree} tree The owner tree
24872         * @param {Node} this This node
24873         * @param {Node} node The newly appended node
24874         * @param {Number} index The index of the newly appended node
24875         */
24876        "append" : true,
24877        /**
24878         * @event remove
24879         * Fires when a child node is removed
24880         * @param {Tree} tree The owner tree
24881         * @param {Node} this This node
24882         * @param {Node} node The removed node
24883         */
24884        "remove" : true,
24885        /**
24886         * @event move
24887         * Fires when this node is moved to a new location in the tree
24888         * @param {Tree} tree The owner tree
24889         * @param {Node} this This node
24890         * @param {Node} oldParent The old parent of this node
24891         * @param {Node} newParent The new parent of this node
24892         * @param {Number} index The index it was moved to
24893         */
24894        "move" : true,
24895        /**
24896         * @event insert
24897         * Fires when a new child node is inserted.
24898         * @param {Tree} tree The owner tree
24899         * @param {Node} this This node
24900         * @param {Node} node The child node inserted
24901         * @param {Node} refNode The child node the node was inserted before
24902         */
24903        "insert" : true,
24904        /**
24905         * @event beforeappend
24906         * Fires before a new child is appended, return false to cancel the append.
24907         * @param {Tree} tree The owner tree
24908         * @param {Node} this This node
24909         * @param {Node} node The child node to be appended
24910         */
24911        "beforeappend" : true,
24912        /**
24913         * @event beforeremove
24914         * Fires before a child is removed, return false to cancel the remove.
24915         * @param {Tree} tree The owner tree
24916         * @param {Node} this This node
24917         * @param {Node} node The child node to be removed
24918         */
24919        "beforeremove" : true,
24920        /**
24921         * @event beforemove
24922         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24923         * @param {Tree} tree The owner tree
24924         * @param {Node} this This node
24925         * @param {Node} oldParent The parent of this node
24926         * @param {Node} newParent The new parent this node is moving to
24927         * @param {Number} index The index it is being moved to
24928         */
24929        "beforemove" : true,
24930        /**
24931         * @event beforeinsert
24932         * Fires before a new child is inserted, return false to cancel the insert.
24933         * @param {Tree} tree The owner tree
24934         * @param {Node} this This node
24935         * @param {Node} node The child node to be inserted
24936         * @param {Node} refNode The child node the node is being inserted before
24937         */
24938        "beforeinsert" : true
24939    });
24940     this.listeners = this.attributes.listeners;
24941     Roo.data.Node.superclass.constructor.call(this);
24942 };
24943
24944 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24945     fireEvent : function(evtName){
24946         // first do standard event for this node
24947         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24948             return false;
24949         }
24950         // then bubble it up to the tree if the event wasn't cancelled
24951         var ot = this.getOwnerTree();
24952         if(ot){
24953             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24954                 return false;
24955             }
24956         }
24957         return true;
24958     },
24959
24960     /**
24961      * Returns true if this node is a leaf
24962      * @return {Boolean}
24963      */
24964     isLeaf : function(){
24965         return this.leaf === true;
24966     },
24967
24968     // private
24969     setFirstChild : function(node){
24970         this.firstChild = node;
24971     },
24972
24973     //private
24974     setLastChild : function(node){
24975         this.lastChild = node;
24976     },
24977
24978
24979     /**
24980      * Returns true if this node is the last child of its parent
24981      * @return {Boolean}
24982      */
24983     isLast : function(){
24984        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24985     },
24986
24987     /**
24988      * Returns true if this node is the first child of its parent
24989      * @return {Boolean}
24990      */
24991     isFirst : function(){
24992        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24993     },
24994
24995     hasChildNodes : function(){
24996         return !this.isLeaf() && this.childNodes.length > 0;
24997     },
24998
24999     /**
25000      * Insert node(s) as the last child node of this node.
25001      * @param {Node/Array} node The node or Array of nodes to append
25002      * @return {Node} The appended node if single append, or null if an array was passed
25003      */
25004     appendChild : function(node){
25005         var multi = false;
25006         if(node instanceof Array){
25007             multi = node;
25008         }else if(arguments.length > 1){
25009             multi = arguments;
25010         }
25011         
25012         // if passed an array or multiple args do them one by one
25013         if(multi){
25014             for(var i = 0, len = multi.length; i < len; i++) {
25015                 this.appendChild(multi[i]);
25016             }
25017         }else{
25018             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25019                 return false;
25020             }
25021             var index = this.childNodes.length;
25022             var oldParent = node.parentNode;
25023             // it's a move, make sure we move it cleanly
25024             if(oldParent){
25025                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25026                     return false;
25027                 }
25028                 oldParent.removeChild(node);
25029             }
25030             
25031             index = this.childNodes.length;
25032             if(index == 0){
25033                 this.setFirstChild(node);
25034             }
25035             this.childNodes.push(node);
25036             node.parentNode = this;
25037             var ps = this.childNodes[index-1];
25038             if(ps){
25039                 node.previousSibling = ps;
25040                 ps.nextSibling = node;
25041             }else{
25042                 node.previousSibling = null;
25043             }
25044             node.nextSibling = null;
25045             this.setLastChild(node);
25046             node.setOwnerTree(this.getOwnerTree());
25047             this.fireEvent("append", this.ownerTree, this, node, index);
25048             if(this.ownerTree) {
25049                 this.ownerTree.fireEvent("appendnode", this, node, index);
25050             }
25051             if(oldParent){
25052                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25053             }
25054             return node;
25055         }
25056     },
25057
25058     /**
25059      * Removes a child node from this node.
25060      * @param {Node} node The node to remove
25061      * @return {Node} The removed node
25062      */
25063     removeChild : function(node){
25064         var index = this.childNodes.indexOf(node);
25065         if(index == -1){
25066             return false;
25067         }
25068         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25069             return false;
25070         }
25071
25072         // remove it from childNodes collection
25073         this.childNodes.splice(index, 1);
25074
25075         // update siblings
25076         if(node.previousSibling){
25077             node.previousSibling.nextSibling = node.nextSibling;
25078         }
25079         if(node.nextSibling){
25080             node.nextSibling.previousSibling = node.previousSibling;
25081         }
25082
25083         // update child refs
25084         if(this.firstChild == node){
25085             this.setFirstChild(node.nextSibling);
25086         }
25087         if(this.lastChild == node){
25088             this.setLastChild(node.previousSibling);
25089         }
25090
25091         node.setOwnerTree(null);
25092         // clear any references from the node
25093         node.parentNode = null;
25094         node.previousSibling = null;
25095         node.nextSibling = null;
25096         this.fireEvent("remove", this.ownerTree, this, node);
25097         return node;
25098     },
25099
25100     /**
25101      * Inserts the first node before the second node in this nodes childNodes collection.
25102      * @param {Node} node The node to insert
25103      * @param {Node} refNode The node to insert before (if null the node is appended)
25104      * @return {Node} The inserted node
25105      */
25106     insertBefore : function(node, refNode){
25107         if(!refNode){ // like standard Dom, refNode can be null for append
25108             return this.appendChild(node);
25109         }
25110         // nothing to do
25111         if(node == refNode){
25112             return false;
25113         }
25114
25115         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25116             return false;
25117         }
25118         var index = this.childNodes.indexOf(refNode);
25119         var oldParent = node.parentNode;
25120         var refIndex = index;
25121
25122         // when moving internally, indexes will change after remove
25123         if(oldParent == this && this.childNodes.indexOf(node) < index){
25124             refIndex--;
25125         }
25126
25127         // it's a move, make sure we move it cleanly
25128         if(oldParent){
25129             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25130                 return false;
25131             }
25132             oldParent.removeChild(node);
25133         }
25134         if(refIndex == 0){
25135             this.setFirstChild(node);
25136         }
25137         this.childNodes.splice(refIndex, 0, node);
25138         node.parentNode = this;
25139         var ps = this.childNodes[refIndex-1];
25140         if(ps){
25141             node.previousSibling = ps;
25142             ps.nextSibling = node;
25143         }else{
25144             node.previousSibling = null;
25145         }
25146         node.nextSibling = refNode;
25147         refNode.previousSibling = node;
25148         node.setOwnerTree(this.getOwnerTree());
25149         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25150         if(oldParent){
25151             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25152         }
25153         return node;
25154     },
25155
25156     /**
25157      * Returns the child node at the specified index.
25158      * @param {Number} index
25159      * @return {Node}
25160      */
25161     item : function(index){
25162         return this.childNodes[index];
25163     },
25164
25165     /**
25166      * Replaces one child node in this node with another.
25167      * @param {Node} newChild The replacement node
25168      * @param {Node} oldChild The node to replace
25169      * @return {Node} The replaced node
25170      */
25171     replaceChild : function(newChild, oldChild){
25172         this.insertBefore(newChild, oldChild);
25173         this.removeChild(oldChild);
25174         return oldChild;
25175     },
25176
25177     /**
25178      * Returns the index of a child node
25179      * @param {Node} node
25180      * @return {Number} The index of the node or -1 if it was not found
25181      */
25182     indexOf : function(child){
25183         return this.childNodes.indexOf(child);
25184     },
25185
25186     /**
25187      * Returns the tree this node is in.
25188      * @return {Tree}
25189      */
25190     getOwnerTree : function(){
25191         // if it doesn't have one, look for one
25192         if(!this.ownerTree){
25193             var p = this;
25194             while(p){
25195                 if(p.ownerTree){
25196                     this.ownerTree = p.ownerTree;
25197                     break;
25198                 }
25199                 p = p.parentNode;
25200             }
25201         }
25202         return this.ownerTree;
25203     },
25204
25205     /**
25206      * Returns depth of this node (the root node has a depth of 0)
25207      * @return {Number}
25208      */
25209     getDepth : function(){
25210         var depth = 0;
25211         var p = this;
25212         while(p.parentNode){
25213             ++depth;
25214             p = p.parentNode;
25215         }
25216         return depth;
25217     },
25218
25219     // private
25220     setOwnerTree : function(tree){
25221         // if it's move, we need to update everyone
25222         if(tree != this.ownerTree){
25223             if(this.ownerTree){
25224                 this.ownerTree.unregisterNode(this);
25225             }
25226             this.ownerTree = tree;
25227             var cs = this.childNodes;
25228             for(var i = 0, len = cs.length; i < len; i++) {
25229                 cs[i].setOwnerTree(tree);
25230             }
25231             if(tree){
25232                 tree.registerNode(this);
25233             }
25234         }
25235     },
25236
25237     /**
25238      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25239      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25240      * @return {String} The path
25241      */
25242     getPath : function(attr){
25243         attr = attr || "id";
25244         var p = this.parentNode;
25245         var b = [this.attributes[attr]];
25246         while(p){
25247             b.unshift(p.attributes[attr]);
25248             p = p.parentNode;
25249         }
25250         var sep = this.getOwnerTree().pathSeparator;
25251         return sep + b.join(sep);
25252     },
25253
25254     /**
25255      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25256      * function call will be the scope provided or the current node. The arguments to the function
25257      * will be the args provided or the current node. If the function returns false at any point,
25258      * the bubble is stopped.
25259      * @param {Function} fn The function to call
25260      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25261      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25262      */
25263     bubble : function(fn, scope, args){
25264         var p = this;
25265         while(p){
25266             if(fn.call(scope || p, args || p) === false){
25267                 break;
25268             }
25269             p = p.parentNode;
25270         }
25271     },
25272
25273     /**
25274      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25275      * function call will be the scope provided or the current node. The arguments to the function
25276      * will be the args provided or the current node. If the function returns false at any point,
25277      * the cascade is stopped on that branch.
25278      * @param {Function} fn The function to call
25279      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25280      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25281      */
25282     cascade : function(fn, scope, args){
25283         if(fn.call(scope || this, args || this) !== false){
25284             var cs = this.childNodes;
25285             for(var i = 0, len = cs.length; i < len; i++) {
25286                 cs[i].cascade(fn, scope, args);
25287             }
25288         }
25289     },
25290
25291     /**
25292      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25293      * function call will be the scope provided or the current node. The arguments to the function
25294      * will be the args provided or the current node. If the function returns false at any point,
25295      * the iteration stops.
25296      * @param {Function} fn The function to call
25297      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25298      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25299      */
25300     eachChild : function(fn, scope, args){
25301         var cs = this.childNodes;
25302         for(var i = 0, len = cs.length; i < len; i++) {
25303                 if(fn.call(scope || this, args || cs[i]) === false){
25304                     break;
25305                 }
25306         }
25307     },
25308
25309     /**
25310      * Finds the first child that has the attribute with the specified value.
25311      * @param {String} attribute The attribute name
25312      * @param {Mixed} value The value to search for
25313      * @return {Node} The found child or null if none was found
25314      */
25315     findChild : function(attribute, value){
25316         var cs = this.childNodes;
25317         for(var i = 0, len = cs.length; i < len; i++) {
25318                 if(cs[i].attributes[attribute] == value){
25319                     return cs[i];
25320                 }
25321         }
25322         return null;
25323     },
25324
25325     /**
25326      * Finds the first child by a custom function. The child matches if the function passed
25327      * returns true.
25328      * @param {Function} fn
25329      * @param {Object} scope (optional)
25330      * @return {Node} The found child or null if none was found
25331      */
25332     findChildBy : function(fn, scope){
25333         var cs = this.childNodes;
25334         for(var i = 0, len = cs.length; i < len; i++) {
25335                 if(fn.call(scope||cs[i], cs[i]) === true){
25336                     return cs[i];
25337                 }
25338         }
25339         return null;
25340     },
25341
25342     /**
25343      * Sorts this nodes children using the supplied sort function
25344      * @param {Function} fn
25345      * @param {Object} scope (optional)
25346      */
25347     sort : function(fn, scope){
25348         var cs = this.childNodes;
25349         var len = cs.length;
25350         if(len > 0){
25351             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25352             cs.sort(sortFn);
25353             for(var i = 0; i < len; i++){
25354                 var n = cs[i];
25355                 n.previousSibling = cs[i-1];
25356                 n.nextSibling = cs[i+1];
25357                 if(i == 0){
25358                     this.setFirstChild(n);
25359                 }
25360                 if(i == len-1){
25361                     this.setLastChild(n);
25362                 }
25363             }
25364         }
25365     },
25366
25367     /**
25368      * Returns true if this node is an ancestor (at any point) of the passed node.
25369      * @param {Node} node
25370      * @return {Boolean}
25371      */
25372     contains : function(node){
25373         return node.isAncestor(this);
25374     },
25375
25376     /**
25377      * Returns true if the passed node is an ancestor (at any point) of this node.
25378      * @param {Node} node
25379      * @return {Boolean}
25380      */
25381     isAncestor : function(node){
25382         var p = this.parentNode;
25383         while(p){
25384             if(p == node){
25385                 return true;
25386             }
25387             p = p.parentNode;
25388         }
25389         return false;
25390     },
25391
25392     toString : function(){
25393         return "[Node"+(this.id?" "+this.id:"")+"]";
25394     }
25395 });/*
25396  * Based on:
25397  * Ext JS Library 1.1.1
25398  * Copyright(c) 2006-2007, Ext JS, LLC.
25399  *
25400  * Originally Released Under LGPL - original licence link has changed is not relivant.
25401  *
25402  * Fork - LGPL
25403  * <script type="text/javascript">
25404  */
25405  (function(){ 
25406 /**
25407  * @class Roo.Layer
25408  * @extends Roo.Element
25409  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25410  * automatic maintaining of shadow/shim positions.
25411  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25412  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25413  * you can pass a string with a CSS class name. False turns off the shadow.
25414  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25415  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25416  * @cfg {String} cls CSS class to add to the element
25417  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25418  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25419  * @constructor
25420  * @param {Object} config An object with config options.
25421  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25422  */
25423
25424 Roo.Layer = function(config, existingEl){
25425     config = config || {};
25426     var dh = Roo.DomHelper;
25427     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25428     if(existingEl){
25429         this.dom = Roo.getDom(existingEl);
25430     }
25431     if(!this.dom){
25432         var o = config.dh || {tag: "div", cls: "x-layer"};
25433         this.dom = dh.append(pel, o);
25434     }
25435     if(config.cls){
25436         this.addClass(config.cls);
25437     }
25438     this.constrain = config.constrain !== false;
25439     this.visibilityMode = Roo.Element.VISIBILITY;
25440     if(config.id){
25441         this.id = this.dom.id = config.id;
25442     }else{
25443         this.id = Roo.id(this.dom);
25444     }
25445     this.zindex = config.zindex || this.getZIndex();
25446     this.position("absolute", this.zindex);
25447     if(config.shadow){
25448         this.shadowOffset = config.shadowOffset || 4;
25449         this.shadow = new Roo.Shadow({
25450             offset : this.shadowOffset,
25451             mode : config.shadow
25452         });
25453     }else{
25454         this.shadowOffset = 0;
25455     }
25456     this.useShim = config.shim !== false && Roo.useShims;
25457     this.useDisplay = config.useDisplay;
25458     this.hide();
25459 };
25460
25461 var supr = Roo.Element.prototype;
25462
25463 // shims are shared among layer to keep from having 100 iframes
25464 var shims = [];
25465
25466 Roo.extend(Roo.Layer, Roo.Element, {
25467
25468     getZIndex : function(){
25469         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25470     },
25471
25472     getShim : function(){
25473         if(!this.useShim){
25474             return null;
25475         }
25476         if(this.shim){
25477             return this.shim;
25478         }
25479         var shim = shims.shift();
25480         if(!shim){
25481             shim = this.createShim();
25482             shim.enableDisplayMode('block');
25483             shim.dom.style.display = 'none';
25484             shim.dom.style.visibility = 'visible';
25485         }
25486         var pn = this.dom.parentNode;
25487         if(shim.dom.parentNode != pn){
25488             pn.insertBefore(shim.dom, this.dom);
25489         }
25490         shim.setStyle('z-index', this.getZIndex()-2);
25491         this.shim = shim;
25492         return shim;
25493     },
25494
25495     hideShim : function(){
25496         if(this.shim){
25497             this.shim.setDisplayed(false);
25498             shims.push(this.shim);
25499             delete this.shim;
25500         }
25501     },
25502
25503     disableShadow : function(){
25504         if(this.shadow){
25505             this.shadowDisabled = true;
25506             this.shadow.hide();
25507             this.lastShadowOffset = this.shadowOffset;
25508             this.shadowOffset = 0;
25509         }
25510     },
25511
25512     enableShadow : function(show){
25513         if(this.shadow){
25514             this.shadowDisabled = false;
25515             this.shadowOffset = this.lastShadowOffset;
25516             delete this.lastShadowOffset;
25517             if(show){
25518                 this.sync(true);
25519             }
25520         }
25521     },
25522
25523     // private
25524     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25525     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25526     sync : function(doShow){
25527         var sw = this.shadow;
25528         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25529             var sh = this.getShim();
25530
25531             var w = this.getWidth(),
25532                 h = this.getHeight();
25533
25534             var l = this.getLeft(true),
25535                 t = this.getTop(true);
25536
25537             if(sw && !this.shadowDisabled){
25538                 if(doShow && !sw.isVisible()){
25539                     sw.show(this);
25540                 }else{
25541                     sw.realign(l, t, w, h);
25542                 }
25543                 if(sh){
25544                     if(doShow){
25545                        sh.show();
25546                     }
25547                     // fit the shim behind the shadow, so it is shimmed too
25548                     var a = sw.adjusts, s = sh.dom.style;
25549                     s.left = (Math.min(l, l+a.l))+"px";
25550                     s.top = (Math.min(t, t+a.t))+"px";
25551                     s.width = (w+a.w)+"px";
25552                     s.height = (h+a.h)+"px";
25553                 }
25554             }else if(sh){
25555                 if(doShow){
25556                    sh.show();
25557                 }
25558                 sh.setSize(w, h);
25559                 sh.setLeftTop(l, t);
25560             }
25561             
25562         }
25563     },
25564
25565     // private
25566     destroy : function(){
25567         this.hideShim();
25568         if(this.shadow){
25569             this.shadow.hide();
25570         }
25571         this.removeAllListeners();
25572         var pn = this.dom.parentNode;
25573         if(pn){
25574             pn.removeChild(this.dom);
25575         }
25576         Roo.Element.uncache(this.id);
25577     },
25578
25579     remove : function(){
25580         this.destroy();
25581     },
25582
25583     // private
25584     beginUpdate : function(){
25585         this.updating = true;
25586     },
25587
25588     // private
25589     endUpdate : function(){
25590         this.updating = false;
25591         this.sync(true);
25592     },
25593
25594     // private
25595     hideUnders : function(negOffset){
25596         if(this.shadow){
25597             this.shadow.hide();
25598         }
25599         this.hideShim();
25600     },
25601
25602     // private
25603     constrainXY : function(){
25604         if(this.constrain){
25605             var vw = Roo.lib.Dom.getViewWidth(),
25606                 vh = Roo.lib.Dom.getViewHeight();
25607             var s = Roo.get(document).getScroll();
25608
25609             var xy = this.getXY();
25610             var x = xy[0], y = xy[1];   
25611             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25612             // only move it if it needs it
25613             var moved = false;
25614             // first validate right/bottom
25615             if((x + w) > vw+s.left){
25616                 x = vw - w - this.shadowOffset;
25617                 moved = true;
25618             }
25619             if((y + h) > vh+s.top){
25620                 y = vh - h - this.shadowOffset;
25621                 moved = true;
25622             }
25623             // then make sure top/left isn't negative
25624             if(x < s.left){
25625                 x = s.left;
25626                 moved = true;
25627             }
25628             if(y < s.top){
25629                 y = s.top;
25630                 moved = true;
25631             }
25632             if(moved){
25633                 if(this.avoidY){
25634                     var ay = this.avoidY;
25635                     if(y <= ay && (y+h) >= ay){
25636                         y = ay-h-5;   
25637                     }
25638                 }
25639                 xy = [x, y];
25640                 this.storeXY(xy);
25641                 supr.setXY.call(this, xy);
25642                 this.sync();
25643             }
25644         }
25645     },
25646
25647     isVisible : function(){
25648         return this.visible;    
25649     },
25650
25651     // private
25652     showAction : function(){
25653         this.visible = true; // track visibility to prevent getStyle calls
25654         if(this.useDisplay === true){
25655             this.setDisplayed("");
25656         }else if(this.lastXY){
25657             supr.setXY.call(this, this.lastXY);
25658         }else if(this.lastLT){
25659             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25660         }
25661     },
25662
25663     // private
25664     hideAction : function(){
25665         this.visible = false;
25666         if(this.useDisplay === true){
25667             this.setDisplayed(false);
25668         }else{
25669             this.setLeftTop(-10000,-10000);
25670         }
25671     },
25672
25673     // overridden Element method
25674     setVisible : function(v, a, d, c, e){
25675         if(v){
25676             this.showAction();
25677         }
25678         if(a && v){
25679             var cb = function(){
25680                 this.sync(true);
25681                 if(c){
25682                     c();
25683                 }
25684             }.createDelegate(this);
25685             supr.setVisible.call(this, true, true, d, cb, e);
25686         }else{
25687             if(!v){
25688                 this.hideUnders(true);
25689             }
25690             var cb = c;
25691             if(a){
25692                 cb = function(){
25693                     this.hideAction();
25694                     if(c){
25695                         c();
25696                     }
25697                 }.createDelegate(this);
25698             }
25699             supr.setVisible.call(this, v, a, d, cb, e);
25700             if(v){
25701                 this.sync(true);
25702             }else if(!a){
25703                 this.hideAction();
25704             }
25705         }
25706     },
25707
25708     storeXY : function(xy){
25709         delete this.lastLT;
25710         this.lastXY = xy;
25711     },
25712
25713     storeLeftTop : function(left, top){
25714         delete this.lastXY;
25715         this.lastLT = [left, top];
25716     },
25717
25718     // private
25719     beforeFx : function(){
25720         this.beforeAction();
25721         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25722     },
25723
25724     // private
25725     afterFx : function(){
25726         Roo.Layer.superclass.afterFx.apply(this, arguments);
25727         this.sync(this.isVisible());
25728     },
25729
25730     // private
25731     beforeAction : function(){
25732         if(!this.updating && this.shadow){
25733             this.shadow.hide();
25734         }
25735     },
25736
25737     // overridden Element method
25738     setLeft : function(left){
25739         this.storeLeftTop(left, this.getTop(true));
25740         supr.setLeft.apply(this, arguments);
25741         this.sync();
25742     },
25743
25744     setTop : function(top){
25745         this.storeLeftTop(this.getLeft(true), top);
25746         supr.setTop.apply(this, arguments);
25747         this.sync();
25748     },
25749
25750     setLeftTop : function(left, top){
25751         this.storeLeftTop(left, top);
25752         supr.setLeftTop.apply(this, arguments);
25753         this.sync();
25754     },
25755
25756     setXY : function(xy, a, d, c, e){
25757         this.fixDisplay();
25758         this.beforeAction();
25759         this.storeXY(xy);
25760         var cb = this.createCB(c);
25761         supr.setXY.call(this, xy, a, d, cb, e);
25762         if(!a){
25763             cb();
25764         }
25765     },
25766
25767     // private
25768     createCB : function(c){
25769         var el = this;
25770         return function(){
25771             el.constrainXY();
25772             el.sync(true);
25773             if(c){
25774                 c();
25775             }
25776         };
25777     },
25778
25779     // overridden Element method
25780     setX : function(x, a, d, c, e){
25781         this.setXY([x, this.getY()], a, d, c, e);
25782     },
25783
25784     // overridden Element method
25785     setY : function(y, a, d, c, e){
25786         this.setXY([this.getX(), y], a, d, c, e);
25787     },
25788
25789     // overridden Element method
25790     setSize : function(w, h, a, d, c, e){
25791         this.beforeAction();
25792         var cb = this.createCB(c);
25793         supr.setSize.call(this, w, h, a, d, cb, e);
25794         if(!a){
25795             cb();
25796         }
25797     },
25798
25799     // overridden Element method
25800     setWidth : function(w, a, d, c, e){
25801         this.beforeAction();
25802         var cb = this.createCB(c);
25803         supr.setWidth.call(this, w, a, d, cb, e);
25804         if(!a){
25805             cb();
25806         }
25807     },
25808
25809     // overridden Element method
25810     setHeight : function(h, a, d, c, e){
25811         this.beforeAction();
25812         var cb = this.createCB(c);
25813         supr.setHeight.call(this, h, a, d, cb, e);
25814         if(!a){
25815             cb();
25816         }
25817     },
25818
25819     // overridden Element method
25820     setBounds : function(x, y, w, h, a, d, c, e){
25821         this.beforeAction();
25822         var cb = this.createCB(c);
25823         if(!a){
25824             this.storeXY([x, y]);
25825             supr.setXY.call(this, [x, y]);
25826             supr.setSize.call(this, w, h, a, d, cb, e);
25827             cb();
25828         }else{
25829             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25830         }
25831         return this;
25832     },
25833     
25834     /**
25835      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25836      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25837      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25838      * @param {Number} zindex The new z-index to set
25839      * @return {this} The Layer
25840      */
25841     setZIndex : function(zindex){
25842         this.zindex = zindex;
25843         this.setStyle("z-index", zindex + 2);
25844         if(this.shadow){
25845             this.shadow.setZIndex(zindex + 1);
25846         }
25847         if(this.shim){
25848             this.shim.setStyle("z-index", zindex);
25849         }
25850     }
25851 });
25852 })();/*
25853  * Based on:
25854  * Ext JS Library 1.1.1
25855  * Copyright(c) 2006-2007, Ext JS, LLC.
25856  *
25857  * Originally Released Under LGPL - original licence link has changed is not relivant.
25858  *
25859  * Fork - LGPL
25860  * <script type="text/javascript">
25861  */
25862
25863
25864 /**
25865  * @class Roo.Shadow
25866  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25867  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25868  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25869  * @constructor
25870  * Create a new Shadow
25871  * @param {Object} config The config object
25872  */
25873 Roo.Shadow = function(config){
25874     Roo.apply(this, config);
25875     if(typeof this.mode != "string"){
25876         this.mode = this.defaultMode;
25877     }
25878     var o = this.offset, a = {h: 0};
25879     var rad = Math.floor(this.offset/2);
25880     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25881         case "drop":
25882             a.w = 0;
25883             a.l = a.t = o;
25884             a.t -= 1;
25885             if(Roo.isIE){
25886                 a.l -= this.offset + rad;
25887                 a.t -= this.offset + rad;
25888                 a.w -= rad;
25889                 a.h -= rad;
25890                 a.t += 1;
25891             }
25892         break;
25893         case "sides":
25894             a.w = (o*2);
25895             a.l = -o;
25896             a.t = o-1;
25897             if(Roo.isIE){
25898                 a.l -= (this.offset - rad);
25899                 a.t -= this.offset + rad;
25900                 a.l += 1;
25901                 a.w -= (this.offset - rad)*2;
25902                 a.w -= rad + 1;
25903                 a.h -= 1;
25904             }
25905         break;
25906         case "frame":
25907             a.w = a.h = (o*2);
25908             a.l = a.t = -o;
25909             a.t += 1;
25910             a.h -= 2;
25911             if(Roo.isIE){
25912                 a.l -= (this.offset - rad);
25913                 a.t -= (this.offset - rad);
25914                 a.l += 1;
25915                 a.w -= (this.offset + rad + 1);
25916                 a.h -= (this.offset + rad);
25917                 a.h += 1;
25918             }
25919         break;
25920     };
25921
25922     this.adjusts = a;
25923 };
25924
25925 Roo.Shadow.prototype = {
25926     /**
25927      * @cfg {String} mode
25928      * The shadow display mode.  Supports the following options:<br />
25929      * sides: Shadow displays on both sides and bottom only<br />
25930      * frame: Shadow displays equally on all four sides<br />
25931      * drop: Traditional bottom-right drop shadow (default)
25932      */
25933     /**
25934      * @cfg {String} offset
25935      * The number of pixels to offset the shadow from the element (defaults to 4)
25936      */
25937     offset: 4,
25938
25939     // private
25940     defaultMode: "drop",
25941
25942     /**
25943      * Displays the shadow under the target element
25944      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25945      */
25946     show : function(target){
25947         target = Roo.get(target);
25948         if(!this.el){
25949             this.el = Roo.Shadow.Pool.pull();
25950             if(this.el.dom.nextSibling != target.dom){
25951                 this.el.insertBefore(target);
25952             }
25953         }
25954         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25955         if(Roo.isIE){
25956             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25957         }
25958         this.realign(
25959             target.getLeft(true),
25960             target.getTop(true),
25961             target.getWidth(),
25962             target.getHeight()
25963         );
25964         this.el.dom.style.display = "block";
25965     },
25966
25967     /**
25968      * Returns true if the shadow is visible, else false
25969      */
25970     isVisible : function(){
25971         return this.el ? true : false;  
25972     },
25973
25974     /**
25975      * Direct alignment when values are already available. Show must be called at least once before
25976      * calling this method to ensure it is initialized.
25977      * @param {Number} left The target element left position
25978      * @param {Number} top The target element top position
25979      * @param {Number} width The target element width
25980      * @param {Number} height The target element height
25981      */
25982     realign : function(l, t, w, h){
25983         if(!this.el){
25984             return;
25985         }
25986         var a = this.adjusts, d = this.el.dom, s = d.style;
25987         var iea = 0;
25988         s.left = (l+a.l)+"px";
25989         s.top = (t+a.t)+"px";
25990         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25991  
25992         if(s.width != sws || s.height != shs){
25993             s.width = sws;
25994             s.height = shs;
25995             if(!Roo.isIE){
25996                 var cn = d.childNodes;
25997                 var sww = Math.max(0, (sw-12))+"px";
25998                 cn[0].childNodes[1].style.width = sww;
25999                 cn[1].childNodes[1].style.width = sww;
26000                 cn[2].childNodes[1].style.width = sww;
26001                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26002             }
26003         }
26004     },
26005
26006     /**
26007      * Hides this shadow
26008      */
26009     hide : function(){
26010         if(this.el){
26011             this.el.dom.style.display = "none";
26012             Roo.Shadow.Pool.push(this.el);
26013             delete this.el;
26014         }
26015     },
26016
26017     /**
26018      * Adjust the z-index of this shadow
26019      * @param {Number} zindex The new z-index
26020      */
26021     setZIndex : function(z){
26022         this.zIndex = z;
26023         if(this.el){
26024             this.el.setStyle("z-index", z);
26025         }
26026     }
26027 };
26028
26029 // Private utility class that manages the internal Shadow cache
26030 Roo.Shadow.Pool = function(){
26031     var p = [];
26032     var markup = Roo.isIE ?
26033                  '<div class="x-ie-shadow"></div>' :
26034                  '<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>';
26035     return {
26036         pull : function(){
26037             var sh = p.shift();
26038             if(!sh){
26039                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26040                 sh.autoBoxAdjust = false;
26041             }
26042             return sh;
26043         },
26044
26045         push : function(sh){
26046             p.push(sh);
26047         }
26048     };
26049 }();/*
26050  * Based on:
26051  * Ext JS Library 1.1.1
26052  * Copyright(c) 2006-2007, Ext JS, LLC.
26053  *
26054  * Originally Released Under LGPL - original licence link has changed is not relivant.
26055  *
26056  * Fork - LGPL
26057  * <script type="text/javascript">
26058  */
26059
26060
26061 /**
26062  * @class Roo.SplitBar
26063  * @extends Roo.util.Observable
26064  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26065  * <br><br>
26066  * Usage:
26067  * <pre><code>
26068 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26069                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26070 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26071 split.minSize = 100;
26072 split.maxSize = 600;
26073 split.animate = true;
26074 split.on('moved', splitterMoved);
26075 </code></pre>
26076  * @constructor
26077  * Create a new SplitBar
26078  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26079  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26080  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26081  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26082                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26083                         position of the SplitBar).
26084  */
26085 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26086     
26087     /** @private */
26088     this.el = Roo.get(dragElement, true);
26089     this.el.dom.unselectable = "on";
26090     /** @private */
26091     this.resizingEl = Roo.get(resizingElement, true);
26092
26093     /**
26094      * @private
26095      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26096      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26097      * @type Number
26098      */
26099     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26100     
26101     /**
26102      * The minimum size of the resizing element. (Defaults to 0)
26103      * @type Number
26104      */
26105     this.minSize = 0;
26106     
26107     /**
26108      * The maximum size of the resizing element. (Defaults to 2000)
26109      * @type Number
26110      */
26111     this.maxSize = 2000;
26112     
26113     /**
26114      * Whether to animate the transition to the new size
26115      * @type Boolean
26116      */
26117     this.animate = false;
26118     
26119     /**
26120      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26121      * @type Boolean
26122      */
26123     this.useShim = false;
26124     
26125     /** @private */
26126     this.shim = null;
26127     
26128     if(!existingProxy){
26129         /** @private */
26130         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26131     }else{
26132         this.proxy = Roo.get(existingProxy).dom;
26133     }
26134     /** @private */
26135     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26136     
26137     /** @private */
26138     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26139     
26140     /** @private */
26141     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26142     
26143     /** @private */
26144     this.dragSpecs = {};
26145     
26146     /**
26147      * @private The adapter to use to positon and resize elements
26148      */
26149     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26150     this.adapter.init(this);
26151     
26152     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26153         /** @private */
26154         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26155         this.el.addClass("x-splitbar-h");
26156     }else{
26157         /** @private */
26158         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26159         this.el.addClass("x-splitbar-v");
26160     }
26161     
26162     this.addEvents({
26163         /**
26164          * @event resize
26165          * Fires when the splitter is moved (alias for {@link #event-moved})
26166          * @param {Roo.SplitBar} this
26167          * @param {Number} newSize the new width or height
26168          */
26169         "resize" : true,
26170         /**
26171          * @event moved
26172          * Fires when the splitter is moved
26173          * @param {Roo.SplitBar} this
26174          * @param {Number} newSize the new width or height
26175          */
26176         "moved" : true,
26177         /**
26178          * @event beforeresize
26179          * Fires before the splitter is dragged
26180          * @param {Roo.SplitBar} this
26181          */
26182         "beforeresize" : true,
26183
26184         "beforeapply" : true
26185     });
26186
26187     Roo.util.Observable.call(this);
26188 };
26189
26190 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26191     onStartProxyDrag : function(x, y){
26192         this.fireEvent("beforeresize", this);
26193         if(!this.overlay){
26194             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26195             o.unselectable();
26196             o.enableDisplayMode("block");
26197             // all splitbars share the same overlay
26198             Roo.SplitBar.prototype.overlay = o;
26199         }
26200         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26201         this.overlay.show();
26202         Roo.get(this.proxy).setDisplayed("block");
26203         var size = this.adapter.getElementSize(this);
26204         this.activeMinSize = this.getMinimumSize();;
26205         this.activeMaxSize = this.getMaximumSize();;
26206         var c1 = size - this.activeMinSize;
26207         var c2 = Math.max(this.activeMaxSize - size, 0);
26208         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26209             this.dd.resetConstraints();
26210             this.dd.setXConstraint(
26211                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26212                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26213             );
26214             this.dd.setYConstraint(0, 0);
26215         }else{
26216             this.dd.resetConstraints();
26217             this.dd.setXConstraint(0, 0);
26218             this.dd.setYConstraint(
26219                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26220                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26221             );
26222          }
26223         this.dragSpecs.startSize = size;
26224         this.dragSpecs.startPoint = [x, y];
26225         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26226     },
26227     
26228     /** 
26229      * @private Called after the drag operation by the DDProxy
26230      */
26231     onEndProxyDrag : function(e){
26232         Roo.get(this.proxy).setDisplayed(false);
26233         var endPoint = Roo.lib.Event.getXY(e);
26234         if(this.overlay){
26235             this.overlay.hide();
26236         }
26237         var newSize;
26238         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26239             newSize = this.dragSpecs.startSize + 
26240                 (this.placement == Roo.SplitBar.LEFT ?
26241                     endPoint[0] - this.dragSpecs.startPoint[0] :
26242                     this.dragSpecs.startPoint[0] - endPoint[0]
26243                 );
26244         }else{
26245             newSize = this.dragSpecs.startSize + 
26246                 (this.placement == Roo.SplitBar.TOP ?
26247                     endPoint[1] - this.dragSpecs.startPoint[1] :
26248                     this.dragSpecs.startPoint[1] - endPoint[1]
26249                 );
26250         }
26251         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26252         if(newSize != this.dragSpecs.startSize){
26253             if(this.fireEvent('beforeapply', this, newSize) !== false){
26254                 this.adapter.setElementSize(this, newSize);
26255                 this.fireEvent("moved", this, newSize);
26256                 this.fireEvent("resize", this, newSize);
26257             }
26258         }
26259     },
26260     
26261     /**
26262      * Get the adapter this SplitBar uses
26263      * @return The adapter object
26264      */
26265     getAdapter : function(){
26266         return this.adapter;
26267     },
26268     
26269     /**
26270      * Set the adapter this SplitBar uses
26271      * @param {Object} adapter A SplitBar adapter object
26272      */
26273     setAdapter : function(adapter){
26274         this.adapter = adapter;
26275         this.adapter.init(this);
26276     },
26277     
26278     /**
26279      * Gets the minimum size for the resizing element
26280      * @return {Number} The minimum size
26281      */
26282     getMinimumSize : function(){
26283         return this.minSize;
26284     },
26285     
26286     /**
26287      * Sets the minimum size for the resizing element
26288      * @param {Number} minSize The minimum size
26289      */
26290     setMinimumSize : function(minSize){
26291         this.minSize = minSize;
26292     },
26293     
26294     /**
26295      * Gets the maximum size for the resizing element
26296      * @return {Number} The maximum size
26297      */
26298     getMaximumSize : function(){
26299         return this.maxSize;
26300     },
26301     
26302     /**
26303      * Sets the maximum size for the resizing element
26304      * @param {Number} maxSize The maximum size
26305      */
26306     setMaximumSize : function(maxSize){
26307         this.maxSize = maxSize;
26308     },
26309     
26310     /**
26311      * Sets the initialize size for the resizing element
26312      * @param {Number} size The initial size
26313      */
26314     setCurrentSize : function(size){
26315         var oldAnimate = this.animate;
26316         this.animate = false;
26317         this.adapter.setElementSize(this, size);
26318         this.animate = oldAnimate;
26319     },
26320     
26321     /**
26322      * Destroy this splitbar. 
26323      * @param {Boolean} removeEl True to remove the element
26324      */
26325     destroy : function(removeEl){
26326         if(this.shim){
26327             this.shim.remove();
26328         }
26329         this.dd.unreg();
26330         this.proxy.parentNode.removeChild(this.proxy);
26331         if(removeEl){
26332             this.el.remove();
26333         }
26334     }
26335 });
26336
26337 /**
26338  * @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.
26339  */
26340 Roo.SplitBar.createProxy = function(dir){
26341     var proxy = new Roo.Element(document.createElement("div"));
26342     proxy.unselectable();
26343     var cls = 'x-splitbar-proxy';
26344     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26345     document.body.appendChild(proxy.dom);
26346     return proxy.dom;
26347 };
26348
26349 /** 
26350  * @class Roo.SplitBar.BasicLayoutAdapter
26351  * Default Adapter. It assumes the splitter and resizing element are not positioned
26352  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26353  */
26354 Roo.SplitBar.BasicLayoutAdapter = function(){
26355 };
26356
26357 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26358     // do nothing for now
26359     init : function(s){
26360     
26361     },
26362     /**
26363      * Called before drag operations to get the current size of the resizing element. 
26364      * @param {Roo.SplitBar} s The SplitBar using this adapter
26365      */
26366      getElementSize : function(s){
26367         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26368             return s.resizingEl.getWidth();
26369         }else{
26370             return s.resizingEl.getHeight();
26371         }
26372     },
26373     
26374     /**
26375      * Called after drag operations to set the size of the resizing element.
26376      * @param {Roo.SplitBar} s The SplitBar using this adapter
26377      * @param {Number} newSize The new size to set
26378      * @param {Function} onComplete A function to be invoked when resizing is complete
26379      */
26380     setElementSize : function(s, newSize, onComplete){
26381         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26382             if(!s.animate){
26383                 s.resizingEl.setWidth(newSize);
26384                 if(onComplete){
26385                     onComplete(s, newSize);
26386                 }
26387             }else{
26388                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26389             }
26390         }else{
26391             
26392             if(!s.animate){
26393                 s.resizingEl.setHeight(newSize);
26394                 if(onComplete){
26395                     onComplete(s, newSize);
26396                 }
26397             }else{
26398                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26399             }
26400         }
26401     }
26402 };
26403
26404 /** 
26405  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26406  * @extends Roo.SplitBar.BasicLayoutAdapter
26407  * Adapter that  moves the splitter element to align with the resized sizing element. 
26408  * Used with an absolute positioned SplitBar.
26409  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26410  * document.body, make sure you assign an id to the body element.
26411  */
26412 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26413     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26414     this.container = Roo.get(container);
26415 };
26416
26417 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26418     init : function(s){
26419         this.basic.init(s);
26420     },
26421     
26422     getElementSize : function(s){
26423         return this.basic.getElementSize(s);
26424     },
26425     
26426     setElementSize : function(s, newSize, onComplete){
26427         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26428     },
26429     
26430     moveSplitter : function(s){
26431         var yes = Roo.SplitBar;
26432         switch(s.placement){
26433             case yes.LEFT:
26434                 s.el.setX(s.resizingEl.getRight());
26435                 break;
26436             case yes.RIGHT:
26437                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26438                 break;
26439             case yes.TOP:
26440                 s.el.setY(s.resizingEl.getBottom());
26441                 break;
26442             case yes.BOTTOM:
26443                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26444                 break;
26445         }
26446     }
26447 };
26448
26449 /**
26450  * Orientation constant - Create a vertical SplitBar
26451  * @static
26452  * @type Number
26453  */
26454 Roo.SplitBar.VERTICAL = 1;
26455
26456 /**
26457  * Orientation constant - Create a horizontal SplitBar
26458  * @static
26459  * @type Number
26460  */
26461 Roo.SplitBar.HORIZONTAL = 2;
26462
26463 /**
26464  * Placement constant - The resizing element is to the left of the splitter element
26465  * @static
26466  * @type Number
26467  */
26468 Roo.SplitBar.LEFT = 1;
26469
26470 /**
26471  * Placement constant - The resizing element is to the right of the splitter element
26472  * @static
26473  * @type Number
26474  */
26475 Roo.SplitBar.RIGHT = 2;
26476
26477 /**
26478  * Placement constant - The resizing element is positioned above the splitter element
26479  * @static
26480  * @type Number
26481  */
26482 Roo.SplitBar.TOP = 3;
26483
26484 /**
26485  * Placement constant - The resizing element is positioned under splitter element
26486  * @static
26487  * @type Number
26488  */
26489 Roo.SplitBar.BOTTOM = 4;
26490 /*
26491  * Based on:
26492  * Ext JS Library 1.1.1
26493  * Copyright(c) 2006-2007, Ext JS, LLC.
26494  *
26495  * Originally Released Under LGPL - original licence link has changed is not relivant.
26496  *
26497  * Fork - LGPL
26498  * <script type="text/javascript">
26499  */
26500
26501 /**
26502  * @class Roo.View
26503  * @extends Roo.util.Observable
26504  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26505  * This class also supports single and multi selection modes. <br>
26506  * Create a data model bound view:
26507  <pre><code>
26508  var store = new Roo.data.Store(...);
26509
26510  var view = new Roo.View({
26511     el : "my-element",
26512     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26513  
26514     singleSelect: true,
26515     selectedClass: "ydataview-selected",
26516     store: store
26517  });
26518
26519  // listen for node click?
26520  view.on("click", function(vw, index, node, e){
26521  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26522  });
26523
26524  // load XML data
26525  dataModel.load("foobar.xml");
26526  </code></pre>
26527  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26528  * <br><br>
26529  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26530  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26531  * 
26532  * Note: old style constructor is still suported (container, template, config)
26533  * 
26534  * @constructor
26535  * Create a new View
26536  * @param {Object} config The config object
26537  * 
26538  */
26539 Roo.View = function(config, depreciated_tpl, depreciated_config){
26540     
26541     this.parent = false;
26542     
26543     if (typeof(depreciated_tpl) == 'undefined') {
26544         // new way.. - universal constructor.
26545         Roo.apply(this, config);
26546         this.el  = Roo.get(this.el);
26547     } else {
26548         // old format..
26549         this.el  = Roo.get(config);
26550         this.tpl = depreciated_tpl;
26551         Roo.apply(this, depreciated_config);
26552     }
26553     this.wrapEl  = this.el.wrap().wrap();
26554     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26555     
26556     
26557     if(typeof(this.tpl) == "string"){
26558         this.tpl = new Roo.Template(this.tpl);
26559     } else {
26560         // support xtype ctors..
26561         this.tpl = new Roo.factory(this.tpl, Roo);
26562     }
26563     
26564     
26565     this.tpl.compile();
26566     
26567     /** @private */
26568     this.addEvents({
26569         /**
26570          * @event beforeclick
26571          * Fires before a click is processed. Returns false to cancel the default action.
26572          * @param {Roo.View} this
26573          * @param {Number} index The index of the target node
26574          * @param {HTMLElement} node The target node
26575          * @param {Roo.EventObject} e The raw event object
26576          */
26577             "beforeclick" : true,
26578         /**
26579          * @event click
26580          * Fires when a template node is clicked.
26581          * @param {Roo.View} this
26582          * @param {Number} index The index of the target node
26583          * @param {HTMLElement} node The target node
26584          * @param {Roo.EventObject} e The raw event object
26585          */
26586             "click" : true,
26587         /**
26588          * @event dblclick
26589          * Fires when a template node is double clicked.
26590          * @param {Roo.View} this
26591          * @param {Number} index The index of the target node
26592          * @param {HTMLElement} node The target node
26593          * @param {Roo.EventObject} e The raw event object
26594          */
26595             "dblclick" : true,
26596         /**
26597          * @event contextmenu
26598          * Fires when a template node is right clicked.
26599          * @param {Roo.View} this
26600          * @param {Number} index The index of the target node
26601          * @param {HTMLElement} node The target node
26602          * @param {Roo.EventObject} e The raw event object
26603          */
26604             "contextmenu" : true,
26605         /**
26606          * @event selectionchange
26607          * Fires when the selected nodes change.
26608          * @param {Roo.View} this
26609          * @param {Array} selections Array of the selected nodes
26610          */
26611             "selectionchange" : true,
26612     
26613         /**
26614          * @event beforeselect
26615          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26616          * @param {Roo.View} this
26617          * @param {HTMLElement} node The node to be selected
26618          * @param {Array} selections Array of currently selected nodes
26619          */
26620             "beforeselect" : true,
26621         /**
26622          * @event preparedata
26623          * Fires on every row to render, to allow you to change the data.
26624          * @param {Roo.View} this
26625          * @param {Object} data to be rendered (change this)
26626          */
26627           "preparedata" : true
26628           
26629           
26630         });
26631
26632
26633
26634     this.el.on({
26635         "click": this.onClick,
26636         "dblclick": this.onDblClick,
26637         "contextmenu": this.onContextMenu,
26638         scope:this
26639     });
26640
26641     this.selections = [];
26642     this.nodes = [];
26643     this.cmp = new Roo.CompositeElementLite([]);
26644     if(this.store){
26645         this.store = Roo.factory(this.store, Roo.data);
26646         this.setStore(this.store, true);
26647     }
26648     
26649     if ( this.footer && this.footer.xtype) {
26650            
26651          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26652         
26653         this.footer.dataSource = this.store;
26654         this.footer.container = fctr;
26655         this.footer = Roo.factory(this.footer, Roo);
26656         fctr.insertFirst(this.el);
26657         
26658         // this is a bit insane - as the paging toolbar seems to detach the el..
26659 //        dom.parentNode.parentNode.parentNode
26660          // they get detached?
26661     }
26662     
26663     
26664     Roo.View.superclass.constructor.call(this);
26665     
26666     
26667 };
26668
26669 Roo.extend(Roo.View, Roo.util.Observable, {
26670     
26671      /**
26672      * @cfg {Roo.data.Store} store Data store to load data from.
26673      */
26674     store : false,
26675     
26676     /**
26677      * @cfg {String|Roo.Element} el The container element.
26678      */
26679     el : '',
26680     
26681     /**
26682      * @cfg {String|Roo.Template} tpl The template used by this View 
26683      */
26684     tpl : false,
26685     /**
26686      * @cfg {String} dataName the named area of the template to use as the data area
26687      *                          Works with domtemplates roo-name="name"
26688      */
26689     dataName: false,
26690     /**
26691      * @cfg {String} selectedClass The css class to add to selected nodes
26692      */
26693     selectedClass : "x-view-selected",
26694      /**
26695      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26696      */
26697     emptyText : "",
26698     
26699     /**
26700      * @cfg {String} text to display on mask (default Loading)
26701      */
26702     mask : false,
26703     /**
26704      * @cfg {Boolean} multiSelect Allow multiple selection
26705      */
26706     multiSelect : false,
26707     /**
26708      * @cfg {Boolean} singleSelect Allow single selection
26709      */
26710     singleSelect:  false,
26711     
26712     /**
26713      * @cfg {Boolean} toggleSelect - selecting 
26714      */
26715     toggleSelect : false,
26716     
26717     /**
26718      * @cfg {Boolean} tickable - selecting 
26719      */
26720     tickable : false,
26721     
26722     /**
26723      * Returns the element this view is bound to.
26724      * @return {Roo.Element}
26725      */
26726     getEl : function(){
26727         return this.wrapEl;
26728     },
26729     
26730     
26731
26732     /**
26733      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26734      */
26735     refresh : function(){
26736         //Roo.log('refresh');
26737         var t = this.tpl;
26738         
26739         // if we are using something like 'domtemplate', then
26740         // the what gets used is:
26741         // t.applySubtemplate(NAME, data, wrapping data..)
26742         // the outer template then get' applied with
26743         //     the store 'extra data'
26744         // and the body get's added to the
26745         //      roo-name="data" node?
26746         //      <span class='roo-tpl-{name}'></span> ?????
26747         
26748         
26749         
26750         this.clearSelections();
26751         this.el.update("");
26752         var html = [];
26753         var records = this.store.getRange();
26754         if(records.length < 1) {
26755             
26756             // is this valid??  = should it render a template??
26757             
26758             this.el.update(this.emptyText);
26759             return;
26760         }
26761         var el = this.el;
26762         if (this.dataName) {
26763             this.el.update(t.apply(this.store.meta)); //????
26764             el = this.el.child('.roo-tpl-' + this.dataName);
26765         }
26766         
26767         for(var i = 0, len = records.length; i < len; i++){
26768             var data = this.prepareData(records[i].data, i, records[i]);
26769             this.fireEvent("preparedata", this, data, i, records[i]);
26770             
26771             var d = Roo.apply({}, data);
26772             
26773             if(this.tickable){
26774                 Roo.apply(d, {'roo-id' : Roo.id()});
26775                 
26776                 var _this = this;
26777             
26778                 Roo.each(this.parent.item, function(item){
26779                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26780                         return;
26781                     }
26782                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26783                 });
26784             }
26785             
26786             html[html.length] = Roo.util.Format.trim(
26787                 this.dataName ?
26788                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26789                     t.apply(d)
26790             );
26791         }
26792         
26793         
26794         
26795         el.update(html.join(""));
26796         this.nodes = el.dom.childNodes;
26797         this.updateIndexes(0);
26798     },
26799     
26800
26801     /**
26802      * Function to override to reformat the data that is sent to
26803      * the template for each node.
26804      * DEPRICATED - use the preparedata event handler.
26805      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26806      * a JSON object for an UpdateManager bound view).
26807      */
26808     prepareData : function(data, index, record)
26809     {
26810         this.fireEvent("preparedata", this, data, index, record);
26811         return data;
26812     },
26813
26814     onUpdate : function(ds, record){
26815         // Roo.log('on update');   
26816         this.clearSelections();
26817         var index = this.store.indexOf(record);
26818         var n = this.nodes[index];
26819         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26820         n.parentNode.removeChild(n);
26821         this.updateIndexes(index, index);
26822     },
26823
26824     
26825     
26826 // --------- FIXME     
26827     onAdd : function(ds, records, index)
26828     {
26829         //Roo.log(['on Add', ds, records, index] );        
26830         this.clearSelections();
26831         if(this.nodes.length == 0){
26832             this.refresh();
26833             return;
26834         }
26835         var n = this.nodes[index];
26836         for(var i = 0, len = records.length; i < len; i++){
26837             var d = this.prepareData(records[i].data, i, records[i]);
26838             if(n){
26839                 this.tpl.insertBefore(n, d);
26840             }else{
26841                 
26842                 this.tpl.append(this.el, d);
26843             }
26844         }
26845         this.updateIndexes(index);
26846     },
26847
26848     onRemove : function(ds, record, index){
26849        // Roo.log('onRemove');
26850         this.clearSelections();
26851         var el = this.dataName  ?
26852             this.el.child('.roo-tpl-' + this.dataName) :
26853             this.el; 
26854         
26855         el.dom.removeChild(this.nodes[index]);
26856         this.updateIndexes(index);
26857     },
26858
26859     /**
26860      * Refresh an individual node.
26861      * @param {Number} index
26862      */
26863     refreshNode : function(index){
26864         this.onUpdate(this.store, this.store.getAt(index));
26865     },
26866
26867     updateIndexes : function(startIndex, endIndex){
26868         var ns = this.nodes;
26869         startIndex = startIndex || 0;
26870         endIndex = endIndex || ns.length - 1;
26871         for(var i = startIndex; i <= endIndex; i++){
26872             ns[i].nodeIndex = i;
26873         }
26874     },
26875
26876     /**
26877      * Changes the data store this view uses and refresh the view.
26878      * @param {Store} store
26879      */
26880     setStore : function(store, initial){
26881         if(!initial && this.store){
26882             this.store.un("datachanged", this.refresh);
26883             this.store.un("add", this.onAdd);
26884             this.store.un("remove", this.onRemove);
26885             this.store.un("update", this.onUpdate);
26886             this.store.un("clear", this.refresh);
26887             this.store.un("beforeload", this.onBeforeLoad);
26888             this.store.un("load", this.onLoad);
26889             this.store.un("loadexception", this.onLoad);
26890         }
26891         if(store){
26892           
26893             store.on("datachanged", this.refresh, this);
26894             store.on("add", this.onAdd, this);
26895             store.on("remove", this.onRemove, this);
26896             store.on("update", this.onUpdate, this);
26897             store.on("clear", this.refresh, this);
26898             store.on("beforeload", this.onBeforeLoad, this);
26899             store.on("load", this.onLoad, this);
26900             store.on("loadexception", this.onLoad, this);
26901         }
26902         
26903         if(store){
26904             this.refresh();
26905         }
26906     },
26907     /**
26908      * onbeforeLoad - masks the loading area.
26909      *
26910      */
26911     onBeforeLoad : function(store,opts)
26912     {
26913          //Roo.log('onBeforeLoad');   
26914         if (!opts.add) {
26915             this.el.update("");
26916         }
26917         this.el.mask(this.mask ? this.mask : "Loading" ); 
26918     },
26919     onLoad : function ()
26920     {
26921         this.el.unmask();
26922     },
26923     
26924
26925     /**
26926      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26927      * @param {HTMLElement} node
26928      * @return {HTMLElement} The template node
26929      */
26930     findItemFromChild : function(node){
26931         var el = this.dataName  ?
26932             this.el.child('.roo-tpl-' + this.dataName,true) :
26933             this.el.dom; 
26934         
26935         if(!node || node.parentNode == el){
26936                     return node;
26937             }
26938             var p = node.parentNode;
26939             while(p && p != el){
26940             if(p.parentNode == el){
26941                 return p;
26942             }
26943             p = p.parentNode;
26944         }
26945             return null;
26946     },
26947
26948     /** @ignore */
26949     onClick : function(e){
26950         var item = this.findItemFromChild(e.getTarget());
26951         if(item){
26952             var index = this.indexOf(item);
26953             if(this.onItemClick(item, index, e) !== false){
26954                 this.fireEvent("click", this, index, item, e);
26955             }
26956         }else{
26957             this.clearSelections();
26958         }
26959     },
26960
26961     /** @ignore */
26962     onContextMenu : function(e){
26963         var item = this.findItemFromChild(e.getTarget());
26964         if(item){
26965             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26966         }
26967     },
26968
26969     /** @ignore */
26970     onDblClick : function(e){
26971         var item = this.findItemFromChild(e.getTarget());
26972         if(item){
26973             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26974         }
26975     },
26976
26977     onItemClick : function(item, index, e)
26978     {
26979         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26980             return false;
26981         }
26982         if (this.toggleSelect) {
26983             var m = this.isSelected(item) ? 'unselect' : 'select';
26984             //Roo.log(m);
26985             var _t = this;
26986             _t[m](item, true, false);
26987             return true;
26988         }
26989         if(this.multiSelect || this.singleSelect){
26990             if(this.multiSelect && e.shiftKey && this.lastSelection){
26991                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26992             }else{
26993                 this.select(item, this.multiSelect && e.ctrlKey);
26994                 this.lastSelection = item;
26995             }
26996             
26997             if(!this.tickable){
26998                 e.preventDefault();
26999             }
27000             
27001         }
27002         return true;
27003     },
27004
27005     /**
27006      * Get the number of selected nodes.
27007      * @return {Number}
27008      */
27009     getSelectionCount : function(){
27010         return this.selections.length;
27011     },
27012
27013     /**
27014      * Get the currently selected nodes.
27015      * @return {Array} An array of HTMLElements
27016      */
27017     getSelectedNodes : function(){
27018         return this.selections;
27019     },
27020
27021     /**
27022      * Get the indexes of the selected nodes.
27023      * @return {Array}
27024      */
27025     getSelectedIndexes : function(){
27026         var indexes = [], s = this.selections;
27027         for(var i = 0, len = s.length; i < len; i++){
27028             indexes.push(s[i].nodeIndex);
27029         }
27030         return indexes;
27031     },
27032
27033     /**
27034      * Clear all selections
27035      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27036      */
27037     clearSelections : function(suppressEvent){
27038         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27039             this.cmp.elements = this.selections;
27040             this.cmp.removeClass(this.selectedClass);
27041             this.selections = [];
27042             if(!suppressEvent){
27043                 this.fireEvent("selectionchange", this, this.selections);
27044             }
27045         }
27046     },
27047
27048     /**
27049      * Returns true if the passed node is selected
27050      * @param {HTMLElement/Number} node The node or node index
27051      * @return {Boolean}
27052      */
27053     isSelected : function(node){
27054         var s = this.selections;
27055         if(s.length < 1){
27056             return false;
27057         }
27058         node = this.getNode(node);
27059         return s.indexOf(node) !== -1;
27060     },
27061
27062     /**
27063      * Selects nodes.
27064      * @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
27065      * @param {Boolean} keepExisting (optional) true to keep existing selections
27066      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27067      */
27068     select : function(nodeInfo, keepExisting, suppressEvent){
27069         if(nodeInfo instanceof Array){
27070             if(!keepExisting){
27071                 this.clearSelections(true);
27072             }
27073             for(var i = 0, len = nodeInfo.length; i < len; i++){
27074                 this.select(nodeInfo[i], true, true);
27075             }
27076             return;
27077         } 
27078         var node = this.getNode(nodeInfo);
27079         if(!node || this.isSelected(node)){
27080             return; // already selected.
27081         }
27082         if(!keepExisting){
27083             this.clearSelections(true);
27084         }
27085         
27086         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27087             Roo.fly(node).addClass(this.selectedClass);
27088             this.selections.push(node);
27089             if(!suppressEvent){
27090                 this.fireEvent("selectionchange", this, this.selections);
27091             }
27092         }
27093         
27094         
27095     },
27096       /**
27097      * Unselects nodes.
27098      * @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
27099      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27100      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27101      */
27102     unselect : function(nodeInfo, keepExisting, suppressEvent)
27103     {
27104         if(nodeInfo instanceof Array){
27105             Roo.each(this.selections, function(s) {
27106                 this.unselect(s, nodeInfo);
27107             }, this);
27108             return;
27109         }
27110         var node = this.getNode(nodeInfo);
27111         if(!node || !this.isSelected(node)){
27112             //Roo.log("not selected");
27113             return; // not selected.
27114         }
27115         // fireevent???
27116         var ns = [];
27117         Roo.each(this.selections, function(s) {
27118             if (s == node ) {
27119                 Roo.fly(node).removeClass(this.selectedClass);
27120
27121                 return;
27122             }
27123             ns.push(s);
27124         },this);
27125         
27126         this.selections= ns;
27127         this.fireEvent("selectionchange", this, this.selections);
27128     },
27129
27130     /**
27131      * Gets a template node.
27132      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27133      * @return {HTMLElement} The node or null if it wasn't found
27134      */
27135     getNode : function(nodeInfo){
27136         if(typeof nodeInfo == "string"){
27137             return document.getElementById(nodeInfo);
27138         }else if(typeof nodeInfo == "number"){
27139             return this.nodes[nodeInfo];
27140         }
27141         return nodeInfo;
27142     },
27143
27144     /**
27145      * Gets a range template nodes.
27146      * @param {Number} startIndex
27147      * @param {Number} endIndex
27148      * @return {Array} An array of nodes
27149      */
27150     getNodes : function(start, end){
27151         var ns = this.nodes;
27152         start = start || 0;
27153         end = typeof end == "undefined" ? ns.length - 1 : end;
27154         var nodes = [];
27155         if(start <= end){
27156             for(var i = start; i <= end; i++){
27157                 nodes.push(ns[i]);
27158             }
27159         } else{
27160             for(var i = start; i >= end; i--){
27161                 nodes.push(ns[i]);
27162             }
27163         }
27164         return nodes;
27165     },
27166
27167     /**
27168      * Finds the index of the passed node
27169      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27170      * @return {Number} The index of the node or -1
27171      */
27172     indexOf : function(node){
27173         node = this.getNode(node);
27174         if(typeof node.nodeIndex == "number"){
27175             return node.nodeIndex;
27176         }
27177         var ns = this.nodes;
27178         for(var i = 0, len = ns.length; i < len; i++){
27179             if(ns[i] == node){
27180                 return i;
27181             }
27182         }
27183         return -1;
27184     }
27185 });
27186 /*
27187  * Based on:
27188  * Ext JS Library 1.1.1
27189  * Copyright(c) 2006-2007, Ext JS, LLC.
27190  *
27191  * Originally Released Under LGPL - original licence link has changed is not relivant.
27192  *
27193  * Fork - LGPL
27194  * <script type="text/javascript">
27195  */
27196
27197 /**
27198  * @class Roo.JsonView
27199  * @extends Roo.View
27200  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27201 <pre><code>
27202 var view = new Roo.JsonView({
27203     container: "my-element",
27204     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27205     multiSelect: true, 
27206     jsonRoot: "data" 
27207 });
27208
27209 // listen for node click?
27210 view.on("click", function(vw, index, node, e){
27211     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27212 });
27213
27214 // direct load of JSON data
27215 view.load("foobar.php");
27216
27217 // Example from my blog list
27218 var tpl = new Roo.Template(
27219     '&lt;div class="entry"&gt;' +
27220     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27221     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27222     "&lt;/div&gt;&lt;hr /&gt;"
27223 );
27224
27225 var moreView = new Roo.JsonView({
27226     container :  "entry-list", 
27227     template : tpl,
27228     jsonRoot: "posts"
27229 });
27230 moreView.on("beforerender", this.sortEntries, this);
27231 moreView.load({
27232     url: "/blog/get-posts.php",
27233     params: "allposts=true",
27234     text: "Loading Blog Entries..."
27235 });
27236 </code></pre>
27237
27238 * Note: old code is supported with arguments : (container, template, config)
27239
27240
27241  * @constructor
27242  * Create a new JsonView
27243  * 
27244  * @param {Object} config The config object
27245  * 
27246  */
27247 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27248     
27249     
27250     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27251
27252     var um = this.el.getUpdateManager();
27253     um.setRenderer(this);
27254     um.on("update", this.onLoad, this);
27255     um.on("failure", this.onLoadException, this);
27256
27257     /**
27258      * @event beforerender
27259      * Fires before rendering of the downloaded JSON data.
27260      * @param {Roo.JsonView} this
27261      * @param {Object} data The JSON data loaded
27262      */
27263     /**
27264      * @event load
27265      * Fires when data is loaded.
27266      * @param {Roo.JsonView} this
27267      * @param {Object} data The JSON data loaded
27268      * @param {Object} response The raw Connect response object
27269      */
27270     /**
27271      * @event loadexception
27272      * Fires when loading fails.
27273      * @param {Roo.JsonView} this
27274      * @param {Object} response The raw Connect response object
27275      */
27276     this.addEvents({
27277         'beforerender' : true,
27278         'load' : true,
27279         'loadexception' : true
27280     });
27281 };
27282 Roo.extend(Roo.JsonView, Roo.View, {
27283     /**
27284      * @type {String} The root property in the loaded JSON object that contains the data
27285      */
27286     jsonRoot : "",
27287
27288     /**
27289      * Refreshes the view.
27290      */
27291     refresh : function(){
27292         this.clearSelections();
27293         this.el.update("");
27294         var html = [];
27295         var o = this.jsonData;
27296         if(o && o.length > 0){
27297             for(var i = 0, len = o.length; i < len; i++){
27298                 var data = this.prepareData(o[i], i, o);
27299                 html[html.length] = this.tpl.apply(data);
27300             }
27301         }else{
27302             html.push(this.emptyText);
27303         }
27304         this.el.update(html.join(""));
27305         this.nodes = this.el.dom.childNodes;
27306         this.updateIndexes(0);
27307     },
27308
27309     /**
27310      * 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.
27311      * @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:
27312      <pre><code>
27313      view.load({
27314          url: "your-url.php",
27315          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27316          callback: yourFunction,
27317          scope: yourObject, //(optional scope)
27318          discardUrl: false,
27319          nocache: false,
27320          text: "Loading...",
27321          timeout: 30,
27322          scripts: false
27323      });
27324      </code></pre>
27325      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27326      * 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.
27327      * @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}
27328      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27329      * @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.
27330      */
27331     load : function(){
27332         var um = this.el.getUpdateManager();
27333         um.update.apply(um, arguments);
27334     },
27335
27336     // note - render is a standard framework call...
27337     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27338     render : function(el, response){
27339         
27340         this.clearSelections();
27341         this.el.update("");
27342         var o;
27343         try{
27344             if (response != '') {
27345                 o = Roo.util.JSON.decode(response.responseText);
27346                 if(this.jsonRoot){
27347                     
27348                     o = o[this.jsonRoot];
27349                 }
27350             }
27351         } catch(e){
27352         }
27353         /**
27354          * The current JSON data or null
27355          */
27356         this.jsonData = o;
27357         this.beforeRender();
27358         this.refresh();
27359     },
27360
27361 /**
27362  * Get the number of records in the current JSON dataset
27363  * @return {Number}
27364  */
27365     getCount : function(){
27366         return this.jsonData ? this.jsonData.length : 0;
27367     },
27368
27369 /**
27370  * Returns the JSON object for the specified node(s)
27371  * @param {HTMLElement/Array} node The node or an array of nodes
27372  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27373  * you get the JSON object for the node
27374  */
27375     getNodeData : function(node){
27376         if(node instanceof Array){
27377             var data = [];
27378             for(var i = 0, len = node.length; i < len; i++){
27379                 data.push(this.getNodeData(node[i]));
27380             }
27381             return data;
27382         }
27383         return this.jsonData[this.indexOf(node)] || null;
27384     },
27385
27386     beforeRender : function(){
27387         this.snapshot = this.jsonData;
27388         if(this.sortInfo){
27389             this.sort.apply(this, this.sortInfo);
27390         }
27391         this.fireEvent("beforerender", this, this.jsonData);
27392     },
27393
27394     onLoad : function(el, o){
27395         this.fireEvent("load", this, this.jsonData, o);
27396     },
27397
27398     onLoadException : function(el, o){
27399         this.fireEvent("loadexception", this, o);
27400     },
27401
27402 /**
27403  * Filter the data by a specific property.
27404  * @param {String} property A property on your JSON objects
27405  * @param {String/RegExp} value Either string that the property values
27406  * should start with, or a RegExp to test against the property
27407  */
27408     filter : function(property, value){
27409         if(this.jsonData){
27410             var data = [];
27411             var ss = this.snapshot;
27412             if(typeof value == "string"){
27413                 var vlen = value.length;
27414                 if(vlen == 0){
27415                     this.clearFilter();
27416                     return;
27417                 }
27418                 value = value.toLowerCase();
27419                 for(var i = 0, len = ss.length; i < len; i++){
27420                     var o = ss[i];
27421                     if(o[property].substr(0, vlen).toLowerCase() == value){
27422                         data.push(o);
27423                     }
27424                 }
27425             } else if(value.exec){ // regex?
27426                 for(var i = 0, len = ss.length; i < len; i++){
27427                     var o = ss[i];
27428                     if(value.test(o[property])){
27429                         data.push(o);
27430                     }
27431                 }
27432             } else{
27433                 return;
27434             }
27435             this.jsonData = data;
27436             this.refresh();
27437         }
27438     },
27439
27440 /**
27441  * Filter by a function. The passed function will be called with each
27442  * object in the current dataset. If the function returns true the value is kept,
27443  * otherwise it is filtered.
27444  * @param {Function} fn
27445  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27446  */
27447     filterBy : function(fn, scope){
27448         if(this.jsonData){
27449             var data = [];
27450             var ss = this.snapshot;
27451             for(var i = 0, len = ss.length; i < len; i++){
27452                 var o = ss[i];
27453                 if(fn.call(scope || this, o)){
27454                     data.push(o);
27455                 }
27456             }
27457             this.jsonData = data;
27458             this.refresh();
27459         }
27460     },
27461
27462 /**
27463  * Clears the current filter.
27464  */
27465     clearFilter : function(){
27466         if(this.snapshot && this.jsonData != this.snapshot){
27467             this.jsonData = this.snapshot;
27468             this.refresh();
27469         }
27470     },
27471
27472
27473 /**
27474  * Sorts the data for this view and refreshes it.
27475  * @param {String} property A property on your JSON objects to sort on
27476  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27477  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27478  */
27479     sort : function(property, dir, sortType){
27480         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27481         if(this.jsonData){
27482             var p = property;
27483             var dsc = dir && dir.toLowerCase() == "desc";
27484             var f = function(o1, o2){
27485                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27486                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27487                 ;
27488                 if(v1 < v2){
27489                     return dsc ? +1 : -1;
27490                 } else if(v1 > v2){
27491                     return dsc ? -1 : +1;
27492                 } else{
27493                     return 0;
27494                 }
27495             };
27496             this.jsonData.sort(f);
27497             this.refresh();
27498             if(this.jsonData != this.snapshot){
27499                 this.snapshot.sort(f);
27500             }
27501         }
27502     }
27503 });/*
27504  * Based on:
27505  * Ext JS Library 1.1.1
27506  * Copyright(c) 2006-2007, Ext JS, LLC.
27507  *
27508  * Originally Released Under LGPL - original licence link has changed is not relivant.
27509  *
27510  * Fork - LGPL
27511  * <script type="text/javascript">
27512  */
27513  
27514
27515 /**
27516  * @class Roo.ColorPalette
27517  * @extends Roo.Component
27518  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27519  * Here's an example of typical usage:
27520  * <pre><code>
27521 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27522 cp.render('my-div');
27523
27524 cp.on('select', function(palette, selColor){
27525     // do something with selColor
27526 });
27527 </code></pre>
27528  * @constructor
27529  * Create a new ColorPalette
27530  * @param {Object} config The config object
27531  */
27532 Roo.ColorPalette = function(config){
27533     Roo.ColorPalette.superclass.constructor.call(this, config);
27534     this.addEvents({
27535         /**
27536              * @event select
27537              * Fires when a color is selected
27538              * @param {ColorPalette} this
27539              * @param {String} color The 6-digit color hex code (without the # symbol)
27540              */
27541         select: true
27542     });
27543
27544     if(this.handler){
27545         this.on("select", this.handler, this.scope, true);
27546     }
27547 };
27548 Roo.extend(Roo.ColorPalette, Roo.Component, {
27549     /**
27550      * @cfg {String} itemCls
27551      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27552      */
27553     itemCls : "x-color-palette",
27554     /**
27555      * @cfg {String} value
27556      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27557      * the hex codes are case-sensitive.
27558      */
27559     value : null,
27560     clickEvent:'click',
27561     // private
27562     ctype: "Roo.ColorPalette",
27563
27564     /**
27565      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27566      */
27567     allowReselect : false,
27568
27569     /**
27570      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27571      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27572      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27573      * of colors with the width setting until the box is symmetrical.</p>
27574      * <p>You can override individual colors if needed:</p>
27575      * <pre><code>
27576 var cp = new Roo.ColorPalette();
27577 cp.colors[0] = "FF0000";  // change the first box to red
27578 </code></pre>
27579
27580 Or you can provide a custom array of your own for complete control:
27581 <pre><code>
27582 var cp = new Roo.ColorPalette();
27583 cp.colors = ["000000", "993300", "333300"];
27584 </code></pre>
27585      * @type Array
27586      */
27587     colors : [
27588         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27589         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27590         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27591         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27592         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27593     ],
27594
27595     // private
27596     onRender : function(container, position){
27597         var t = new Roo.MasterTemplate(
27598             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27599         );
27600         var c = this.colors;
27601         for(var i = 0, len = c.length; i < len; i++){
27602             t.add([c[i]]);
27603         }
27604         var el = document.createElement("div");
27605         el.className = this.itemCls;
27606         t.overwrite(el);
27607         container.dom.insertBefore(el, position);
27608         this.el = Roo.get(el);
27609         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27610         if(this.clickEvent != 'click'){
27611             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27612         }
27613     },
27614
27615     // private
27616     afterRender : function(){
27617         Roo.ColorPalette.superclass.afterRender.call(this);
27618         if(this.value){
27619             var s = this.value;
27620             this.value = null;
27621             this.select(s);
27622         }
27623     },
27624
27625     // private
27626     handleClick : function(e, t){
27627         e.preventDefault();
27628         if(!this.disabled){
27629             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27630             this.select(c.toUpperCase());
27631         }
27632     },
27633
27634     /**
27635      * Selects the specified color in the palette (fires the select event)
27636      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27637      */
27638     select : function(color){
27639         color = color.replace("#", "");
27640         if(color != this.value || this.allowReselect){
27641             var el = this.el;
27642             if(this.value){
27643                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27644             }
27645             el.child("a.color-"+color).addClass("x-color-palette-sel");
27646             this.value = color;
27647             this.fireEvent("select", this, color);
27648         }
27649     }
27650 });/*
27651  * Based on:
27652  * Ext JS Library 1.1.1
27653  * Copyright(c) 2006-2007, Ext JS, LLC.
27654  *
27655  * Originally Released Under LGPL - original licence link has changed is not relivant.
27656  *
27657  * Fork - LGPL
27658  * <script type="text/javascript">
27659  */
27660  
27661 /**
27662  * @class Roo.DatePicker
27663  * @extends Roo.Component
27664  * Simple date picker class.
27665  * @constructor
27666  * Create a new DatePicker
27667  * @param {Object} config The config object
27668  */
27669 Roo.DatePicker = function(config){
27670     Roo.DatePicker.superclass.constructor.call(this, config);
27671
27672     this.value = config && config.value ?
27673                  config.value.clearTime() : new Date().clearTime();
27674
27675     this.addEvents({
27676         /**
27677              * @event select
27678              * Fires when a date is selected
27679              * @param {DatePicker} this
27680              * @param {Date} date The selected date
27681              */
27682         'select': true,
27683         /**
27684              * @event monthchange
27685              * Fires when the displayed month changes 
27686              * @param {DatePicker} this
27687              * @param {Date} date The selected month
27688              */
27689         'monthchange': true
27690     });
27691
27692     if(this.handler){
27693         this.on("select", this.handler,  this.scope || this);
27694     }
27695     // build the disabledDatesRE
27696     if(!this.disabledDatesRE && this.disabledDates){
27697         var dd = this.disabledDates;
27698         var re = "(?:";
27699         for(var i = 0; i < dd.length; i++){
27700             re += dd[i];
27701             if(i != dd.length-1) {
27702                 re += "|";
27703             }
27704         }
27705         this.disabledDatesRE = new RegExp(re + ")");
27706     }
27707 };
27708
27709 Roo.extend(Roo.DatePicker, Roo.Component, {
27710     /**
27711      * @cfg {String} todayText
27712      * The text to display on the button that selects the current date (defaults to "Today")
27713      */
27714     todayText : "Today",
27715     /**
27716      * @cfg {String} okText
27717      * The text to display on the ok button
27718      */
27719     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27720     /**
27721      * @cfg {String} cancelText
27722      * The text to display on the cancel button
27723      */
27724     cancelText : "Cancel",
27725     /**
27726      * @cfg {String} todayTip
27727      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27728      */
27729     todayTip : "{0} (Spacebar)",
27730     /**
27731      * @cfg {Date} minDate
27732      * Minimum allowable date (JavaScript date object, defaults to null)
27733      */
27734     minDate : null,
27735     /**
27736      * @cfg {Date} maxDate
27737      * Maximum allowable date (JavaScript date object, defaults to null)
27738      */
27739     maxDate : null,
27740     /**
27741      * @cfg {String} minText
27742      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27743      */
27744     minText : "This date is before the minimum date",
27745     /**
27746      * @cfg {String} maxText
27747      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27748      */
27749     maxText : "This date is after the maximum date",
27750     /**
27751      * @cfg {String} format
27752      * The default date format string which can be overriden for localization support.  The format must be
27753      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27754      */
27755     format : "m/d/y",
27756     /**
27757      * @cfg {Array} disabledDays
27758      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27759      */
27760     disabledDays : null,
27761     /**
27762      * @cfg {String} disabledDaysText
27763      * The tooltip to display when the date falls on a disabled day (defaults to "")
27764      */
27765     disabledDaysText : "",
27766     /**
27767      * @cfg {RegExp} disabledDatesRE
27768      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27769      */
27770     disabledDatesRE : null,
27771     /**
27772      * @cfg {String} disabledDatesText
27773      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27774      */
27775     disabledDatesText : "",
27776     /**
27777      * @cfg {Boolean} constrainToViewport
27778      * True to constrain the date picker to the viewport (defaults to true)
27779      */
27780     constrainToViewport : true,
27781     /**
27782      * @cfg {Array} monthNames
27783      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27784      */
27785     monthNames : Date.monthNames,
27786     /**
27787      * @cfg {Array} dayNames
27788      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27789      */
27790     dayNames : Date.dayNames,
27791     /**
27792      * @cfg {String} nextText
27793      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27794      */
27795     nextText: 'Next Month (Control+Right)',
27796     /**
27797      * @cfg {String} prevText
27798      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27799      */
27800     prevText: 'Previous Month (Control+Left)',
27801     /**
27802      * @cfg {String} monthYearText
27803      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27804      */
27805     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27806     /**
27807      * @cfg {Number} startDay
27808      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27809      */
27810     startDay : 0,
27811     /**
27812      * @cfg {Bool} showClear
27813      * Show a clear button (usefull for date form elements that can be blank.)
27814      */
27815     
27816     showClear: false,
27817     
27818     /**
27819      * Sets the value of the date field
27820      * @param {Date} value The date to set
27821      */
27822     setValue : function(value){
27823         var old = this.value;
27824         
27825         if (typeof(value) == 'string') {
27826          
27827             value = Date.parseDate(value, this.format);
27828         }
27829         if (!value) {
27830             value = new Date();
27831         }
27832         
27833         this.value = value.clearTime(true);
27834         if(this.el){
27835             this.update(this.value);
27836         }
27837     },
27838
27839     /**
27840      * Gets the current selected value of the date field
27841      * @return {Date} The selected date
27842      */
27843     getValue : function(){
27844         return this.value;
27845     },
27846
27847     // private
27848     focus : function(){
27849         if(this.el){
27850             this.update(this.activeDate);
27851         }
27852     },
27853
27854     // privateval
27855     onRender : function(container, position){
27856         
27857         var m = [
27858              '<table cellspacing="0">',
27859                 '<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>',
27860                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27861         var dn = this.dayNames;
27862         for(var i = 0; i < 7; i++){
27863             var d = this.startDay+i;
27864             if(d > 6){
27865                 d = d-7;
27866             }
27867             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27868         }
27869         m[m.length] = "</tr></thead><tbody><tr>";
27870         for(var i = 0; i < 42; i++) {
27871             if(i % 7 == 0 && i != 0){
27872                 m[m.length] = "</tr><tr>";
27873             }
27874             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27875         }
27876         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27877             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27878
27879         var el = document.createElement("div");
27880         el.className = "x-date-picker";
27881         el.innerHTML = m.join("");
27882
27883         container.dom.insertBefore(el, position);
27884
27885         this.el = Roo.get(el);
27886         this.eventEl = Roo.get(el.firstChild);
27887
27888         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27889             handler: this.showPrevMonth,
27890             scope: this,
27891             preventDefault:true,
27892             stopDefault:true
27893         });
27894
27895         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27896             handler: this.showNextMonth,
27897             scope: this,
27898             preventDefault:true,
27899             stopDefault:true
27900         });
27901
27902         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27903
27904         this.monthPicker = this.el.down('div.x-date-mp');
27905         this.monthPicker.enableDisplayMode('block');
27906         
27907         var kn = new Roo.KeyNav(this.eventEl, {
27908             "left" : function(e){
27909                 e.ctrlKey ?
27910                     this.showPrevMonth() :
27911                     this.update(this.activeDate.add("d", -1));
27912             },
27913
27914             "right" : function(e){
27915                 e.ctrlKey ?
27916                     this.showNextMonth() :
27917                     this.update(this.activeDate.add("d", 1));
27918             },
27919
27920             "up" : function(e){
27921                 e.ctrlKey ?
27922                     this.showNextYear() :
27923                     this.update(this.activeDate.add("d", -7));
27924             },
27925
27926             "down" : function(e){
27927                 e.ctrlKey ?
27928                     this.showPrevYear() :
27929                     this.update(this.activeDate.add("d", 7));
27930             },
27931
27932             "pageUp" : function(e){
27933                 this.showNextMonth();
27934             },
27935
27936             "pageDown" : function(e){
27937                 this.showPrevMonth();
27938             },
27939
27940             "enter" : function(e){
27941                 e.stopPropagation();
27942                 return true;
27943             },
27944
27945             scope : this
27946         });
27947
27948         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27949
27950         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27951
27952         this.el.unselectable();
27953         
27954         this.cells = this.el.select("table.x-date-inner tbody td");
27955         this.textNodes = this.el.query("table.x-date-inner tbody span");
27956
27957         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27958             text: "&#160;",
27959             tooltip: this.monthYearText
27960         });
27961
27962         this.mbtn.on('click', this.showMonthPicker, this);
27963         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27964
27965
27966         var today = (new Date()).dateFormat(this.format);
27967         
27968         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27969         if (this.showClear) {
27970             baseTb.add( new Roo.Toolbar.Fill());
27971         }
27972         baseTb.add({
27973             text: String.format(this.todayText, today),
27974             tooltip: String.format(this.todayTip, today),
27975             handler: this.selectToday,
27976             scope: this
27977         });
27978         
27979         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27980             
27981         //});
27982         if (this.showClear) {
27983             
27984             baseTb.add( new Roo.Toolbar.Fill());
27985             baseTb.add({
27986                 text: '&#160;',
27987                 cls: 'x-btn-icon x-btn-clear',
27988                 handler: function() {
27989                     //this.value = '';
27990                     this.fireEvent("select", this, '');
27991                 },
27992                 scope: this
27993             });
27994         }
27995         
27996         
27997         if(Roo.isIE){
27998             this.el.repaint();
27999         }
28000         this.update(this.value);
28001     },
28002
28003     createMonthPicker : function(){
28004         if(!this.monthPicker.dom.firstChild){
28005             var buf = ['<table border="0" cellspacing="0">'];
28006             for(var i = 0; i < 6; i++){
28007                 buf.push(
28008                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28009                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28010                     i == 0 ?
28011                     '<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>' :
28012                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28013                 );
28014             }
28015             buf.push(
28016                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28017                     this.okText,
28018                     '</button><button type="button" class="x-date-mp-cancel">',
28019                     this.cancelText,
28020                     '</button></td></tr>',
28021                 '</table>'
28022             );
28023             this.monthPicker.update(buf.join(''));
28024             this.monthPicker.on('click', this.onMonthClick, this);
28025             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28026
28027             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28028             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28029
28030             this.mpMonths.each(function(m, a, i){
28031                 i += 1;
28032                 if((i%2) == 0){
28033                     m.dom.xmonth = 5 + Math.round(i * .5);
28034                 }else{
28035                     m.dom.xmonth = Math.round((i-1) * .5);
28036                 }
28037             });
28038         }
28039     },
28040
28041     showMonthPicker : function(){
28042         this.createMonthPicker();
28043         var size = this.el.getSize();
28044         this.monthPicker.setSize(size);
28045         this.monthPicker.child('table').setSize(size);
28046
28047         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28048         this.updateMPMonth(this.mpSelMonth);
28049         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28050         this.updateMPYear(this.mpSelYear);
28051
28052         this.monthPicker.slideIn('t', {duration:.2});
28053     },
28054
28055     updateMPYear : function(y){
28056         this.mpyear = y;
28057         var ys = this.mpYears.elements;
28058         for(var i = 1; i <= 10; i++){
28059             var td = ys[i-1], y2;
28060             if((i%2) == 0){
28061                 y2 = y + Math.round(i * .5);
28062                 td.firstChild.innerHTML = y2;
28063                 td.xyear = y2;
28064             }else{
28065                 y2 = y - (5-Math.round(i * .5));
28066                 td.firstChild.innerHTML = y2;
28067                 td.xyear = y2;
28068             }
28069             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28070         }
28071     },
28072
28073     updateMPMonth : function(sm){
28074         this.mpMonths.each(function(m, a, i){
28075             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28076         });
28077     },
28078
28079     selectMPMonth: function(m){
28080         
28081     },
28082
28083     onMonthClick : function(e, t){
28084         e.stopEvent();
28085         var el = new Roo.Element(t), pn;
28086         if(el.is('button.x-date-mp-cancel')){
28087             this.hideMonthPicker();
28088         }
28089         else if(el.is('button.x-date-mp-ok')){
28090             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28091             this.hideMonthPicker();
28092         }
28093         else if(pn = el.up('td.x-date-mp-month', 2)){
28094             this.mpMonths.removeClass('x-date-mp-sel');
28095             pn.addClass('x-date-mp-sel');
28096             this.mpSelMonth = pn.dom.xmonth;
28097         }
28098         else if(pn = el.up('td.x-date-mp-year', 2)){
28099             this.mpYears.removeClass('x-date-mp-sel');
28100             pn.addClass('x-date-mp-sel');
28101             this.mpSelYear = pn.dom.xyear;
28102         }
28103         else if(el.is('a.x-date-mp-prev')){
28104             this.updateMPYear(this.mpyear-10);
28105         }
28106         else if(el.is('a.x-date-mp-next')){
28107             this.updateMPYear(this.mpyear+10);
28108         }
28109     },
28110
28111     onMonthDblClick : function(e, t){
28112         e.stopEvent();
28113         var el = new Roo.Element(t), pn;
28114         if(pn = el.up('td.x-date-mp-month', 2)){
28115             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28116             this.hideMonthPicker();
28117         }
28118         else if(pn = el.up('td.x-date-mp-year', 2)){
28119             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28120             this.hideMonthPicker();
28121         }
28122     },
28123
28124     hideMonthPicker : function(disableAnim){
28125         if(this.monthPicker){
28126             if(disableAnim === true){
28127                 this.monthPicker.hide();
28128             }else{
28129                 this.monthPicker.slideOut('t', {duration:.2});
28130             }
28131         }
28132     },
28133
28134     // private
28135     showPrevMonth : function(e){
28136         this.update(this.activeDate.add("mo", -1));
28137     },
28138
28139     // private
28140     showNextMonth : function(e){
28141         this.update(this.activeDate.add("mo", 1));
28142     },
28143
28144     // private
28145     showPrevYear : function(){
28146         this.update(this.activeDate.add("y", -1));
28147     },
28148
28149     // private
28150     showNextYear : function(){
28151         this.update(this.activeDate.add("y", 1));
28152     },
28153
28154     // private
28155     handleMouseWheel : function(e){
28156         var delta = e.getWheelDelta();
28157         if(delta > 0){
28158             this.showPrevMonth();
28159             e.stopEvent();
28160         } else if(delta < 0){
28161             this.showNextMonth();
28162             e.stopEvent();
28163         }
28164     },
28165
28166     // private
28167     handleDateClick : function(e, t){
28168         e.stopEvent();
28169         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28170             this.setValue(new Date(t.dateValue));
28171             this.fireEvent("select", this, this.value);
28172         }
28173     },
28174
28175     // private
28176     selectToday : function(){
28177         this.setValue(new Date().clearTime());
28178         this.fireEvent("select", this, this.value);
28179     },
28180
28181     // private
28182     update : function(date)
28183     {
28184         var vd = this.activeDate;
28185         this.activeDate = date;
28186         if(vd && this.el){
28187             var t = date.getTime();
28188             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28189                 this.cells.removeClass("x-date-selected");
28190                 this.cells.each(function(c){
28191                    if(c.dom.firstChild.dateValue == t){
28192                        c.addClass("x-date-selected");
28193                        setTimeout(function(){
28194                             try{c.dom.firstChild.focus();}catch(e){}
28195                        }, 50);
28196                        return false;
28197                    }
28198                 });
28199                 return;
28200             }
28201         }
28202         
28203         var days = date.getDaysInMonth();
28204         var firstOfMonth = date.getFirstDateOfMonth();
28205         var startingPos = firstOfMonth.getDay()-this.startDay;
28206
28207         if(startingPos <= this.startDay){
28208             startingPos += 7;
28209         }
28210
28211         var pm = date.add("mo", -1);
28212         var prevStart = pm.getDaysInMonth()-startingPos;
28213
28214         var cells = this.cells.elements;
28215         var textEls = this.textNodes;
28216         days += startingPos;
28217
28218         // convert everything to numbers so it's fast
28219         var day = 86400000;
28220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28221         var today = new Date().clearTime().getTime();
28222         var sel = date.clearTime().getTime();
28223         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28224         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28225         var ddMatch = this.disabledDatesRE;
28226         var ddText = this.disabledDatesText;
28227         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28228         var ddaysText = this.disabledDaysText;
28229         var format = this.format;
28230
28231         var setCellClass = function(cal, cell){
28232             cell.title = "";
28233             var t = d.getTime();
28234             cell.firstChild.dateValue = t;
28235             if(t == today){
28236                 cell.className += " x-date-today";
28237                 cell.title = cal.todayText;
28238             }
28239             if(t == sel){
28240                 cell.className += " x-date-selected";
28241                 setTimeout(function(){
28242                     try{cell.firstChild.focus();}catch(e){}
28243                 }, 50);
28244             }
28245             // disabling
28246             if(t < min) {
28247                 cell.className = " x-date-disabled";
28248                 cell.title = cal.minText;
28249                 return;
28250             }
28251             if(t > max) {
28252                 cell.className = " x-date-disabled";
28253                 cell.title = cal.maxText;
28254                 return;
28255             }
28256             if(ddays){
28257                 if(ddays.indexOf(d.getDay()) != -1){
28258                     cell.title = ddaysText;
28259                     cell.className = " x-date-disabled";
28260                 }
28261             }
28262             if(ddMatch && format){
28263                 var fvalue = d.dateFormat(format);
28264                 if(ddMatch.test(fvalue)){
28265                     cell.title = ddText.replace("%0", fvalue);
28266                     cell.className = " x-date-disabled";
28267                 }
28268             }
28269         };
28270
28271         var i = 0;
28272         for(; i < startingPos; i++) {
28273             textEls[i].innerHTML = (++prevStart);
28274             d.setDate(d.getDate()+1);
28275             cells[i].className = "x-date-prevday";
28276             setCellClass(this, cells[i]);
28277         }
28278         for(; i < days; i++){
28279             intDay = i - startingPos + 1;
28280             textEls[i].innerHTML = (intDay);
28281             d.setDate(d.getDate()+1);
28282             cells[i].className = "x-date-active";
28283             setCellClass(this, cells[i]);
28284         }
28285         var extraDays = 0;
28286         for(; i < 42; i++) {
28287              textEls[i].innerHTML = (++extraDays);
28288              d.setDate(d.getDate()+1);
28289              cells[i].className = "x-date-nextday";
28290              setCellClass(this, cells[i]);
28291         }
28292
28293         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28294         this.fireEvent('monthchange', this, date);
28295         
28296         if(!this.internalRender){
28297             var main = this.el.dom.firstChild;
28298             var w = main.offsetWidth;
28299             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28300             Roo.fly(main).setWidth(w);
28301             this.internalRender = true;
28302             // opera does not respect the auto grow header center column
28303             // then, after it gets a width opera refuses to recalculate
28304             // without a second pass
28305             if(Roo.isOpera && !this.secondPass){
28306                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28307                 this.secondPass = true;
28308                 this.update.defer(10, this, [date]);
28309             }
28310         }
28311         
28312         
28313     }
28314 });        /*
28315  * Based on:
28316  * Ext JS Library 1.1.1
28317  * Copyright(c) 2006-2007, Ext JS, LLC.
28318  *
28319  * Originally Released Under LGPL - original licence link has changed is not relivant.
28320  *
28321  * Fork - LGPL
28322  * <script type="text/javascript">
28323  */
28324 /**
28325  * @class Roo.TabPanel
28326  * @extends Roo.util.Observable
28327  * A lightweight tab container.
28328  * <br><br>
28329  * Usage:
28330  * <pre><code>
28331 // basic tabs 1, built from existing content
28332 var tabs = new Roo.TabPanel("tabs1");
28333 tabs.addTab("script", "View Script");
28334 tabs.addTab("markup", "View Markup");
28335 tabs.activate("script");
28336
28337 // more advanced tabs, built from javascript
28338 var jtabs = new Roo.TabPanel("jtabs");
28339 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28340
28341 // set up the UpdateManager
28342 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28343 var updater = tab2.getUpdateManager();
28344 updater.setDefaultUrl("ajax1.htm");
28345 tab2.on('activate', updater.refresh, updater, true);
28346
28347 // Use setUrl for Ajax loading
28348 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28349 tab3.setUrl("ajax2.htm", null, true);
28350
28351 // Disabled tab
28352 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28353 tab4.disable();
28354
28355 jtabs.activate("jtabs-1");
28356  * </code></pre>
28357  * @constructor
28358  * Create a new TabPanel.
28359  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28360  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28361  */
28362 Roo.TabPanel = function(container, config){
28363     /**
28364     * The container element for this TabPanel.
28365     * @type Roo.Element
28366     */
28367     this.el = Roo.get(container, true);
28368     if(config){
28369         if(typeof config == "boolean"){
28370             this.tabPosition = config ? "bottom" : "top";
28371         }else{
28372             Roo.apply(this, config);
28373         }
28374     }
28375     if(this.tabPosition == "bottom"){
28376         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28377         this.el.addClass("x-tabs-bottom");
28378     }
28379     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28380     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28381     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28382     if(Roo.isIE){
28383         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28384     }
28385     if(this.tabPosition != "bottom"){
28386         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28387          * @type Roo.Element
28388          */
28389         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28390         this.el.addClass("x-tabs-top");
28391     }
28392     this.items = [];
28393
28394     this.bodyEl.setStyle("position", "relative");
28395
28396     this.active = null;
28397     this.activateDelegate = this.activate.createDelegate(this);
28398
28399     this.addEvents({
28400         /**
28401          * @event tabchange
28402          * Fires when the active tab changes
28403          * @param {Roo.TabPanel} this
28404          * @param {Roo.TabPanelItem} activePanel The new active tab
28405          */
28406         "tabchange": true,
28407         /**
28408          * @event beforetabchange
28409          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28410          * @param {Roo.TabPanel} this
28411          * @param {Object} e Set cancel to true on this object to cancel the tab change
28412          * @param {Roo.TabPanelItem} tab The tab being changed to
28413          */
28414         "beforetabchange" : true
28415     });
28416
28417     Roo.EventManager.onWindowResize(this.onResize, this);
28418     this.cpad = this.el.getPadding("lr");
28419     this.hiddenCount = 0;
28420
28421
28422     // toolbar on the tabbar support...
28423     if (this.toolbar) {
28424         var tcfg = this.toolbar;
28425         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28426         this.toolbar = new Roo.Toolbar(tcfg);
28427         if (Roo.isSafari) {
28428             var tbl = tcfg.container.child('table', true);
28429             tbl.setAttribute('width', '100%');
28430         }
28431         
28432     }
28433    
28434
28435
28436     Roo.TabPanel.superclass.constructor.call(this);
28437 };
28438
28439 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28440     /*
28441      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28442      */
28443     tabPosition : "top",
28444     /*
28445      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28446      */
28447     currentTabWidth : 0,
28448     /*
28449      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28450      */
28451     minTabWidth : 40,
28452     /*
28453      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28454      */
28455     maxTabWidth : 250,
28456     /*
28457      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28458      */
28459     preferredTabWidth : 175,
28460     /*
28461      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28462      */
28463     resizeTabs : false,
28464     /*
28465      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28466      */
28467     monitorResize : true,
28468     /*
28469      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28470      */
28471     toolbar : false,
28472
28473     /**
28474      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28475      * @param {String} id The id of the div to use <b>or create</b>
28476      * @param {String} text The text for the tab
28477      * @param {String} content (optional) Content to put in the TabPanelItem body
28478      * @param {Boolean} closable (optional) True to create a close icon on the tab
28479      * @return {Roo.TabPanelItem} The created TabPanelItem
28480      */
28481     addTab : function(id, text, content, closable){
28482         var item = new Roo.TabPanelItem(this, id, text, closable);
28483         this.addTabItem(item);
28484         if(content){
28485             item.setContent(content);
28486         }
28487         return item;
28488     },
28489
28490     /**
28491      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28492      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28493      * @return {Roo.TabPanelItem}
28494      */
28495     getTab : function(id){
28496         return this.items[id];
28497     },
28498
28499     /**
28500      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28501      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28502      */
28503     hideTab : function(id){
28504         var t = this.items[id];
28505         if(!t.isHidden()){
28506            t.setHidden(true);
28507            this.hiddenCount++;
28508            this.autoSizeTabs();
28509         }
28510     },
28511
28512     /**
28513      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28514      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28515      */
28516     unhideTab : function(id){
28517         var t = this.items[id];
28518         if(t.isHidden()){
28519            t.setHidden(false);
28520            this.hiddenCount--;
28521            this.autoSizeTabs();
28522         }
28523     },
28524
28525     /**
28526      * Adds an existing {@link Roo.TabPanelItem}.
28527      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28528      */
28529     addTabItem : function(item){
28530         this.items[item.id] = item;
28531         this.items.push(item);
28532         if(this.resizeTabs){
28533            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28534            this.autoSizeTabs();
28535         }else{
28536             item.autoSize();
28537         }
28538     },
28539
28540     /**
28541      * Removes a {@link Roo.TabPanelItem}.
28542      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28543      */
28544     removeTab : function(id){
28545         var items = this.items;
28546         var tab = items[id];
28547         if(!tab) { return; }
28548         var index = items.indexOf(tab);
28549         if(this.active == tab && items.length > 1){
28550             var newTab = this.getNextAvailable(index);
28551             if(newTab) {
28552                 newTab.activate();
28553             }
28554         }
28555         this.stripEl.dom.removeChild(tab.pnode.dom);
28556         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28557             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28558         }
28559         items.splice(index, 1);
28560         delete this.items[tab.id];
28561         tab.fireEvent("close", tab);
28562         tab.purgeListeners();
28563         this.autoSizeTabs();
28564     },
28565
28566     getNextAvailable : function(start){
28567         var items = this.items;
28568         var index = start;
28569         // look for a next tab that will slide over to
28570         // replace the one being removed
28571         while(index < items.length){
28572             var item = items[++index];
28573             if(item && !item.isHidden()){
28574                 return item;
28575             }
28576         }
28577         // if one isn't found select the previous tab (on the left)
28578         index = start;
28579         while(index >= 0){
28580             var item = items[--index];
28581             if(item && !item.isHidden()){
28582                 return item;
28583             }
28584         }
28585         return null;
28586     },
28587
28588     /**
28589      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28590      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28591      */
28592     disableTab : function(id){
28593         var tab = this.items[id];
28594         if(tab && this.active != tab){
28595             tab.disable();
28596         }
28597     },
28598
28599     /**
28600      * Enables a {@link Roo.TabPanelItem} that is disabled.
28601      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28602      */
28603     enableTab : function(id){
28604         var tab = this.items[id];
28605         tab.enable();
28606     },
28607
28608     /**
28609      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28610      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28611      * @return {Roo.TabPanelItem} The TabPanelItem.
28612      */
28613     activate : function(id){
28614         var tab = this.items[id];
28615         if(!tab){
28616             return null;
28617         }
28618         if(tab == this.active || tab.disabled){
28619             return tab;
28620         }
28621         var e = {};
28622         this.fireEvent("beforetabchange", this, e, tab);
28623         if(e.cancel !== true && !tab.disabled){
28624             if(this.active){
28625                 this.active.hide();
28626             }
28627             this.active = this.items[id];
28628             this.active.show();
28629             this.fireEvent("tabchange", this, this.active);
28630         }
28631         return tab;
28632     },
28633
28634     /**
28635      * Gets the active {@link Roo.TabPanelItem}.
28636      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28637      */
28638     getActiveTab : function(){
28639         return this.active;
28640     },
28641
28642     /**
28643      * Updates the tab body element to fit the height of the container element
28644      * for overflow scrolling
28645      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28646      */
28647     syncHeight : function(targetHeight){
28648         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28649         var bm = this.bodyEl.getMargins();
28650         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28651         this.bodyEl.setHeight(newHeight);
28652         return newHeight;
28653     },
28654
28655     onResize : function(){
28656         if(this.monitorResize){
28657             this.autoSizeTabs();
28658         }
28659     },
28660
28661     /**
28662      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28663      */
28664     beginUpdate : function(){
28665         this.updating = true;
28666     },
28667
28668     /**
28669      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28670      */
28671     endUpdate : function(){
28672         this.updating = false;
28673         this.autoSizeTabs();
28674     },
28675
28676     /**
28677      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28678      */
28679     autoSizeTabs : function(){
28680         var count = this.items.length;
28681         var vcount = count - this.hiddenCount;
28682         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28683             return;
28684         }
28685         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28686         var availWidth = Math.floor(w / vcount);
28687         var b = this.stripBody;
28688         if(b.getWidth() > w){
28689             var tabs = this.items;
28690             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28691             if(availWidth < this.minTabWidth){
28692                 /*if(!this.sleft){    // incomplete scrolling code
28693                     this.createScrollButtons();
28694                 }
28695                 this.showScroll();
28696                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28697             }
28698         }else{
28699             if(this.currentTabWidth < this.preferredTabWidth){
28700                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28701             }
28702         }
28703     },
28704
28705     /**
28706      * Returns the number of tabs in this TabPanel.
28707      * @return {Number}
28708      */
28709      getCount : function(){
28710          return this.items.length;
28711      },
28712
28713     /**
28714      * Resizes all the tabs to the passed width
28715      * @param {Number} The new width
28716      */
28717     setTabWidth : function(width){
28718         this.currentTabWidth = width;
28719         for(var i = 0, len = this.items.length; i < len; i++) {
28720                 if(!this.items[i].isHidden()) {
28721                 this.items[i].setWidth(width);
28722             }
28723         }
28724     },
28725
28726     /**
28727      * Destroys this TabPanel
28728      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28729      */
28730     destroy : function(removeEl){
28731         Roo.EventManager.removeResizeListener(this.onResize, this);
28732         for(var i = 0, len = this.items.length; i < len; i++){
28733             this.items[i].purgeListeners();
28734         }
28735         if(removeEl === true){
28736             this.el.update("");
28737             this.el.remove();
28738         }
28739     }
28740 });
28741
28742 /**
28743  * @class Roo.TabPanelItem
28744  * @extends Roo.util.Observable
28745  * Represents an individual item (tab plus body) in a TabPanel.
28746  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28747  * @param {String} id The id of this TabPanelItem
28748  * @param {String} text The text for the tab of this TabPanelItem
28749  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28750  */
28751 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28752     /**
28753      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28754      * @type Roo.TabPanel
28755      */
28756     this.tabPanel = tabPanel;
28757     /**
28758      * The id for this TabPanelItem
28759      * @type String
28760      */
28761     this.id = id;
28762     /** @private */
28763     this.disabled = false;
28764     /** @private */
28765     this.text = text;
28766     /** @private */
28767     this.loaded = false;
28768     this.closable = closable;
28769
28770     /**
28771      * The body element for this TabPanelItem.
28772      * @type Roo.Element
28773      */
28774     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28775     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28776     this.bodyEl.setStyle("display", "block");
28777     this.bodyEl.setStyle("zoom", "1");
28778     this.hideAction();
28779
28780     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28781     /** @private */
28782     this.el = Roo.get(els.el, true);
28783     this.inner = Roo.get(els.inner, true);
28784     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28785     this.pnode = Roo.get(els.el.parentNode, true);
28786     this.el.on("mousedown", this.onTabMouseDown, this);
28787     this.el.on("click", this.onTabClick, this);
28788     /** @private */
28789     if(closable){
28790         var c = Roo.get(els.close, true);
28791         c.dom.title = this.closeText;
28792         c.addClassOnOver("close-over");
28793         c.on("click", this.closeClick, this);
28794      }
28795
28796     this.addEvents({
28797          /**
28798          * @event activate
28799          * Fires when this tab becomes the active tab.
28800          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28801          * @param {Roo.TabPanelItem} this
28802          */
28803         "activate": true,
28804         /**
28805          * @event beforeclose
28806          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28807          * @param {Roo.TabPanelItem} this
28808          * @param {Object} e Set cancel to true on this object to cancel the close.
28809          */
28810         "beforeclose": true,
28811         /**
28812          * @event close
28813          * Fires when this tab is closed.
28814          * @param {Roo.TabPanelItem} this
28815          */
28816          "close": true,
28817         /**
28818          * @event deactivate
28819          * Fires when this tab is no longer the active tab.
28820          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28821          * @param {Roo.TabPanelItem} this
28822          */
28823          "deactivate" : true
28824     });
28825     this.hidden = false;
28826
28827     Roo.TabPanelItem.superclass.constructor.call(this);
28828 };
28829
28830 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28831     purgeListeners : function(){
28832        Roo.util.Observable.prototype.purgeListeners.call(this);
28833        this.el.removeAllListeners();
28834     },
28835     /**
28836      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28837      */
28838     show : function(){
28839         this.pnode.addClass("on");
28840         this.showAction();
28841         if(Roo.isOpera){
28842             this.tabPanel.stripWrap.repaint();
28843         }
28844         this.fireEvent("activate", this.tabPanel, this);
28845     },
28846
28847     /**
28848      * Returns true if this tab is the active tab.
28849      * @return {Boolean}
28850      */
28851     isActive : function(){
28852         return this.tabPanel.getActiveTab() == this;
28853     },
28854
28855     /**
28856      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28857      */
28858     hide : function(){
28859         this.pnode.removeClass("on");
28860         this.hideAction();
28861         this.fireEvent("deactivate", this.tabPanel, this);
28862     },
28863
28864     hideAction : function(){
28865         this.bodyEl.hide();
28866         this.bodyEl.setStyle("position", "absolute");
28867         this.bodyEl.setLeft("-20000px");
28868         this.bodyEl.setTop("-20000px");
28869     },
28870
28871     showAction : function(){
28872         this.bodyEl.setStyle("position", "relative");
28873         this.bodyEl.setTop("");
28874         this.bodyEl.setLeft("");
28875         this.bodyEl.show();
28876     },
28877
28878     /**
28879      * Set the tooltip for the tab.
28880      * @param {String} tooltip The tab's tooltip
28881      */
28882     setTooltip : function(text){
28883         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28884             this.textEl.dom.qtip = text;
28885             this.textEl.dom.removeAttribute('title');
28886         }else{
28887             this.textEl.dom.title = text;
28888         }
28889     },
28890
28891     onTabClick : function(e){
28892         e.preventDefault();
28893         this.tabPanel.activate(this.id);
28894     },
28895
28896     onTabMouseDown : function(e){
28897         e.preventDefault();
28898         this.tabPanel.activate(this.id);
28899     },
28900
28901     getWidth : function(){
28902         return this.inner.getWidth();
28903     },
28904
28905     setWidth : function(width){
28906         var iwidth = width - this.pnode.getPadding("lr");
28907         this.inner.setWidth(iwidth);
28908         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28909         this.pnode.setWidth(width);
28910     },
28911
28912     /**
28913      * Show or hide the tab
28914      * @param {Boolean} hidden True to hide or false to show.
28915      */
28916     setHidden : function(hidden){
28917         this.hidden = hidden;
28918         this.pnode.setStyle("display", hidden ? "none" : "");
28919     },
28920
28921     /**
28922      * Returns true if this tab is "hidden"
28923      * @return {Boolean}
28924      */
28925     isHidden : function(){
28926         return this.hidden;
28927     },
28928
28929     /**
28930      * Returns the text for this tab
28931      * @return {String}
28932      */
28933     getText : function(){
28934         return this.text;
28935     },
28936
28937     autoSize : function(){
28938         //this.el.beginMeasure();
28939         this.textEl.setWidth(1);
28940         /*
28941          *  #2804 [new] Tabs in Roojs
28942          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28943          */
28944         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28945         //this.el.endMeasure();
28946     },
28947
28948     /**
28949      * Sets the text for the tab (Note: this also sets the tooltip text)
28950      * @param {String} text The tab's text and tooltip
28951      */
28952     setText : function(text){
28953         this.text = text;
28954         this.textEl.update(text);
28955         this.setTooltip(text);
28956         if(!this.tabPanel.resizeTabs){
28957             this.autoSize();
28958         }
28959     },
28960     /**
28961      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28962      */
28963     activate : function(){
28964         this.tabPanel.activate(this.id);
28965     },
28966
28967     /**
28968      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28969      */
28970     disable : function(){
28971         if(this.tabPanel.active != this){
28972             this.disabled = true;
28973             this.pnode.addClass("disabled");
28974         }
28975     },
28976
28977     /**
28978      * Enables this TabPanelItem if it was previously disabled.
28979      */
28980     enable : function(){
28981         this.disabled = false;
28982         this.pnode.removeClass("disabled");
28983     },
28984
28985     /**
28986      * Sets the content for this TabPanelItem.
28987      * @param {String} content The content
28988      * @param {Boolean} loadScripts true to look for and load scripts
28989      */
28990     setContent : function(content, loadScripts){
28991         this.bodyEl.update(content, loadScripts);
28992     },
28993
28994     /**
28995      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28996      * @return {Roo.UpdateManager} The UpdateManager
28997      */
28998     getUpdateManager : function(){
28999         return this.bodyEl.getUpdateManager();
29000     },
29001
29002     /**
29003      * Set a URL to be used to load the content for this TabPanelItem.
29004      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29005      * @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)
29006      * @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)
29007      * @return {Roo.UpdateManager} The UpdateManager
29008      */
29009     setUrl : function(url, params, loadOnce){
29010         if(this.refreshDelegate){
29011             this.un('activate', this.refreshDelegate);
29012         }
29013         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29014         this.on("activate", this.refreshDelegate);
29015         return this.bodyEl.getUpdateManager();
29016     },
29017
29018     /** @private */
29019     _handleRefresh : function(url, params, loadOnce){
29020         if(!loadOnce || !this.loaded){
29021             var updater = this.bodyEl.getUpdateManager();
29022             updater.update(url, params, this._setLoaded.createDelegate(this));
29023         }
29024     },
29025
29026     /**
29027      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29028      *   Will fail silently if the setUrl method has not been called.
29029      *   This does not activate the panel, just updates its content.
29030      */
29031     refresh : function(){
29032         if(this.refreshDelegate){
29033            this.loaded = false;
29034            this.refreshDelegate();
29035         }
29036     },
29037
29038     /** @private */
29039     _setLoaded : function(){
29040         this.loaded = true;
29041     },
29042
29043     /** @private */
29044     closeClick : function(e){
29045         var o = {};
29046         e.stopEvent();
29047         this.fireEvent("beforeclose", this, o);
29048         if(o.cancel !== true){
29049             this.tabPanel.removeTab(this.id);
29050         }
29051     },
29052     /**
29053      * The text displayed in the tooltip for the close icon.
29054      * @type String
29055      */
29056     closeText : "Close this tab"
29057 });
29058
29059 /** @private */
29060 Roo.TabPanel.prototype.createStrip = function(container){
29061     var strip = document.createElement("div");
29062     strip.className = "x-tabs-wrap";
29063     container.appendChild(strip);
29064     return strip;
29065 };
29066 /** @private */
29067 Roo.TabPanel.prototype.createStripList = function(strip){
29068     // div wrapper for retard IE
29069     // returns the "tr" element.
29070     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29071         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29072         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29073     return strip.firstChild.firstChild.firstChild.firstChild;
29074 };
29075 /** @private */
29076 Roo.TabPanel.prototype.createBody = function(container){
29077     var body = document.createElement("div");
29078     Roo.id(body, "tab-body");
29079     Roo.fly(body).addClass("x-tabs-body");
29080     container.appendChild(body);
29081     return body;
29082 };
29083 /** @private */
29084 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29085     var body = Roo.getDom(id);
29086     if(!body){
29087         body = document.createElement("div");
29088         body.id = id;
29089     }
29090     Roo.fly(body).addClass("x-tabs-item-body");
29091     bodyEl.insertBefore(body, bodyEl.firstChild);
29092     return body;
29093 };
29094 /** @private */
29095 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29096     var td = document.createElement("td");
29097     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29098     //stripEl.appendChild(td);
29099     if(closable){
29100         td.className = "x-tabs-closable";
29101         if(!this.closeTpl){
29102             this.closeTpl = new Roo.Template(
29103                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29104                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29105                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29106             );
29107         }
29108         var el = this.closeTpl.overwrite(td, {"text": text});
29109         var close = el.getElementsByTagName("div")[0];
29110         var inner = el.getElementsByTagName("em")[0];
29111         return {"el": el, "close": close, "inner": inner};
29112     } else {
29113         if(!this.tabTpl){
29114             this.tabTpl = new Roo.Template(
29115                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29116                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29117             );
29118         }
29119         var el = this.tabTpl.overwrite(td, {"text": text});
29120         var inner = el.getElementsByTagName("em")[0];
29121         return {"el": el, "inner": inner};
29122     }
29123 };/*
29124  * Based on:
29125  * Ext JS Library 1.1.1
29126  * Copyright(c) 2006-2007, Ext JS, LLC.
29127  *
29128  * Originally Released Under LGPL - original licence link has changed is not relivant.
29129  *
29130  * Fork - LGPL
29131  * <script type="text/javascript">
29132  */
29133
29134 /**
29135  * @class Roo.Button
29136  * @extends Roo.util.Observable
29137  * Simple Button class
29138  * @cfg {String} text The button text
29139  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29140  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29141  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29142  * @cfg {Object} scope The scope of the handler
29143  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29144  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29145  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29146  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29147  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29148  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29149    applies if enableToggle = true)
29150  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29151  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29152   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29153  * @constructor
29154  * Create a new button
29155  * @param {Object} config The config object
29156  */
29157 Roo.Button = function(renderTo, config)
29158 {
29159     if (!config) {
29160         config = renderTo;
29161         renderTo = config.renderTo || false;
29162     }
29163     
29164     Roo.apply(this, config);
29165     this.addEvents({
29166         /**
29167              * @event click
29168              * Fires when this button is clicked
29169              * @param {Button} this
29170              * @param {EventObject} e The click event
29171              */
29172             "click" : true,
29173         /**
29174              * @event toggle
29175              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29176              * @param {Button} this
29177              * @param {Boolean} pressed
29178              */
29179             "toggle" : true,
29180         /**
29181              * @event mouseover
29182              * Fires when the mouse hovers over the button
29183              * @param {Button} this
29184              * @param {Event} e The event object
29185              */
29186         'mouseover' : true,
29187         /**
29188              * @event mouseout
29189              * Fires when the mouse exits the button
29190              * @param {Button} this
29191              * @param {Event} e The event object
29192              */
29193         'mouseout': true,
29194          /**
29195              * @event render
29196              * Fires when the button is rendered
29197              * @param {Button} this
29198              */
29199         'render': true
29200     });
29201     if(this.menu){
29202         this.menu = Roo.menu.MenuMgr.get(this.menu);
29203     }
29204     // register listeners first!!  - so render can be captured..
29205     Roo.util.Observable.call(this);
29206     if(renderTo){
29207         this.render(renderTo);
29208     }
29209     
29210   
29211 };
29212
29213 Roo.extend(Roo.Button, Roo.util.Observable, {
29214     /**
29215      * 
29216      */
29217     
29218     /**
29219      * Read-only. True if this button is hidden
29220      * @type Boolean
29221      */
29222     hidden : false,
29223     /**
29224      * Read-only. True if this button is disabled
29225      * @type Boolean
29226      */
29227     disabled : false,
29228     /**
29229      * Read-only. True if this button is pressed (only if enableToggle = true)
29230      * @type Boolean
29231      */
29232     pressed : false,
29233
29234     /**
29235      * @cfg {Number} tabIndex 
29236      * The DOM tabIndex for this button (defaults to undefined)
29237      */
29238     tabIndex : undefined,
29239
29240     /**
29241      * @cfg {Boolean} enableToggle
29242      * True to enable pressed/not pressed toggling (defaults to false)
29243      */
29244     enableToggle: false,
29245     /**
29246      * @cfg {Mixed} menu
29247      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29248      */
29249     menu : undefined,
29250     /**
29251      * @cfg {String} menuAlign
29252      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29253      */
29254     menuAlign : "tl-bl?",
29255
29256     /**
29257      * @cfg {String} iconCls
29258      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29259      */
29260     iconCls : undefined,
29261     /**
29262      * @cfg {String} type
29263      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29264      */
29265     type : 'button',
29266
29267     // private
29268     menuClassTarget: 'tr',
29269
29270     /**
29271      * @cfg {String} clickEvent
29272      * The type of event to map to the button's event handler (defaults to 'click')
29273      */
29274     clickEvent : 'click',
29275
29276     /**
29277      * @cfg {Boolean} handleMouseEvents
29278      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29279      */
29280     handleMouseEvents : true,
29281
29282     /**
29283      * @cfg {String} tooltipType
29284      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29285      */
29286     tooltipType : 'qtip',
29287
29288     /**
29289      * @cfg {String} cls
29290      * A CSS class to apply to the button's main element.
29291      */
29292     
29293     /**
29294      * @cfg {Roo.Template} template (Optional)
29295      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29296      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29297      * require code modifications if required elements (e.g. a button) aren't present.
29298      */
29299
29300     // private
29301     render : function(renderTo){
29302         var btn;
29303         if(this.hideParent){
29304             this.parentEl = Roo.get(renderTo);
29305         }
29306         if(!this.dhconfig){
29307             if(!this.template){
29308                 if(!Roo.Button.buttonTemplate){
29309                     // hideous table template
29310                     Roo.Button.buttonTemplate = new Roo.Template(
29311                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29312                         '<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>',
29313                         "</tr></tbody></table>");
29314                 }
29315                 this.template = Roo.Button.buttonTemplate;
29316             }
29317             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29318             var btnEl = btn.child("button:first");
29319             btnEl.on('focus', this.onFocus, this);
29320             btnEl.on('blur', this.onBlur, this);
29321             if(this.cls){
29322                 btn.addClass(this.cls);
29323             }
29324             if(this.icon){
29325                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29326             }
29327             if(this.iconCls){
29328                 btnEl.addClass(this.iconCls);
29329                 if(!this.cls){
29330                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29331                 }
29332             }
29333             if(this.tabIndex !== undefined){
29334                 btnEl.dom.tabIndex = this.tabIndex;
29335             }
29336             if(this.tooltip){
29337                 if(typeof this.tooltip == 'object'){
29338                     Roo.QuickTips.tips(Roo.apply({
29339                           target: btnEl.id
29340                     }, this.tooltip));
29341                 } else {
29342                     btnEl.dom[this.tooltipType] = this.tooltip;
29343                 }
29344             }
29345         }else{
29346             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29347         }
29348         this.el = btn;
29349         if(this.id){
29350             this.el.dom.id = this.el.id = this.id;
29351         }
29352         if(this.menu){
29353             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29354             this.menu.on("show", this.onMenuShow, this);
29355             this.menu.on("hide", this.onMenuHide, this);
29356         }
29357         btn.addClass("x-btn");
29358         if(Roo.isIE && !Roo.isIE7){
29359             this.autoWidth.defer(1, this);
29360         }else{
29361             this.autoWidth();
29362         }
29363         if(this.handleMouseEvents){
29364             btn.on("mouseover", this.onMouseOver, this);
29365             btn.on("mouseout", this.onMouseOut, this);
29366             btn.on("mousedown", this.onMouseDown, this);
29367         }
29368         btn.on(this.clickEvent, this.onClick, this);
29369         //btn.on("mouseup", this.onMouseUp, this);
29370         if(this.hidden){
29371             this.hide();
29372         }
29373         if(this.disabled){
29374             this.disable();
29375         }
29376         Roo.ButtonToggleMgr.register(this);
29377         if(this.pressed){
29378             this.el.addClass("x-btn-pressed");
29379         }
29380         if(this.repeat){
29381             var repeater = new Roo.util.ClickRepeater(btn,
29382                 typeof this.repeat == "object" ? this.repeat : {}
29383             );
29384             repeater.on("click", this.onClick,  this);
29385         }
29386         
29387         this.fireEvent('render', this);
29388         
29389     },
29390     /**
29391      * Returns the button's underlying element
29392      * @return {Roo.Element} The element
29393      */
29394     getEl : function(){
29395         return this.el;  
29396     },
29397     
29398     /**
29399      * Destroys this Button and removes any listeners.
29400      */
29401     destroy : function(){
29402         Roo.ButtonToggleMgr.unregister(this);
29403         this.el.removeAllListeners();
29404         this.purgeListeners();
29405         this.el.remove();
29406     },
29407
29408     // private
29409     autoWidth : function(){
29410         if(this.el){
29411             this.el.setWidth("auto");
29412             if(Roo.isIE7 && Roo.isStrict){
29413                 var ib = this.el.child('button');
29414                 if(ib && ib.getWidth() > 20){
29415                     ib.clip();
29416                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29417                 }
29418             }
29419             if(this.minWidth){
29420                 if(this.hidden){
29421                     this.el.beginMeasure();
29422                 }
29423                 if(this.el.getWidth() < this.minWidth){
29424                     this.el.setWidth(this.minWidth);
29425                 }
29426                 if(this.hidden){
29427                     this.el.endMeasure();
29428                 }
29429             }
29430         }
29431     },
29432
29433     /**
29434      * Assigns this button's click handler
29435      * @param {Function} handler The function to call when the button is clicked
29436      * @param {Object} scope (optional) Scope for the function passed in
29437      */
29438     setHandler : function(handler, scope){
29439         this.handler = handler;
29440         this.scope = scope;  
29441     },
29442     
29443     /**
29444      * Sets this button's text
29445      * @param {String} text The button text
29446      */
29447     setText : function(text){
29448         this.text = text;
29449         if(this.el){
29450             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29451         }
29452         this.autoWidth();
29453     },
29454     
29455     /**
29456      * Gets the text for this button
29457      * @return {String} The button text
29458      */
29459     getText : function(){
29460         return this.text;  
29461     },
29462     
29463     /**
29464      * Show this button
29465      */
29466     show: function(){
29467         this.hidden = false;
29468         if(this.el){
29469             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29470         }
29471     },
29472     
29473     /**
29474      * Hide this button
29475      */
29476     hide: function(){
29477         this.hidden = true;
29478         if(this.el){
29479             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29480         }
29481     },
29482     
29483     /**
29484      * Convenience function for boolean show/hide
29485      * @param {Boolean} visible True to show, false to hide
29486      */
29487     setVisible: function(visible){
29488         if(visible) {
29489             this.show();
29490         }else{
29491             this.hide();
29492         }
29493     },
29494     
29495     /**
29496      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29497      * @param {Boolean} state (optional) Force a particular state
29498      */
29499     toggle : function(state){
29500         state = state === undefined ? !this.pressed : state;
29501         if(state != this.pressed){
29502             if(state){
29503                 this.el.addClass("x-btn-pressed");
29504                 this.pressed = true;
29505                 this.fireEvent("toggle", this, true);
29506             }else{
29507                 this.el.removeClass("x-btn-pressed");
29508                 this.pressed = false;
29509                 this.fireEvent("toggle", this, false);
29510             }
29511             if(this.toggleHandler){
29512                 this.toggleHandler.call(this.scope || this, this, state);
29513             }
29514         }
29515     },
29516     
29517     /**
29518      * Focus the button
29519      */
29520     focus : function(){
29521         this.el.child('button:first').focus();
29522     },
29523     
29524     /**
29525      * Disable this button
29526      */
29527     disable : function(){
29528         if(this.el){
29529             this.el.addClass("x-btn-disabled");
29530         }
29531         this.disabled = true;
29532     },
29533     
29534     /**
29535      * Enable this button
29536      */
29537     enable : function(){
29538         if(this.el){
29539             this.el.removeClass("x-btn-disabled");
29540         }
29541         this.disabled = false;
29542     },
29543
29544     /**
29545      * Convenience function for boolean enable/disable
29546      * @param {Boolean} enabled True to enable, false to disable
29547      */
29548     setDisabled : function(v){
29549         this[v !== true ? "enable" : "disable"]();
29550     },
29551
29552     // private
29553     onClick : function(e)
29554     {
29555         if(e){
29556             e.preventDefault();
29557         }
29558         if(e.button != 0){
29559             return;
29560         }
29561         if(!this.disabled){
29562             if(this.enableToggle){
29563                 this.toggle();
29564             }
29565             if(this.menu && !this.menu.isVisible()){
29566                 this.menu.show(this.el, this.menuAlign);
29567             }
29568             this.fireEvent("click", this, e);
29569             if(this.handler){
29570                 this.el.removeClass("x-btn-over");
29571                 this.handler.call(this.scope || this, this, e);
29572             }
29573         }
29574     },
29575     // private
29576     onMouseOver : function(e){
29577         if(!this.disabled){
29578             this.el.addClass("x-btn-over");
29579             this.fireEvent('mouseover', this, e);
29580         }
29581     },
29582     // private
29583     onMouseOut : function(e){
29584         if(!e.within(this.el,  true)){
29585             this.el.removeClass("x-btn-over");
29586             this.fireEvent('mouseout', this, e);
29587         }
29588     },
29589     // private
29590     onFocus : function(e){
29591         if(!this.disabled){
29592             this.el.addClass("x-btn-focus");
29593         }
29594     },
29595     // private
29596     onBlur : function(e){
29597         this.el.removeClass("x-btn-focus");
29598     },
29599     // private
29600     onMouseDown : function(e){
29601         if(!this.disabled && e.button == 0){
29602             this.el.addClass("x-btn-click");
29603             Roo.get(document).on('mouseup', this.onMouseUp, this);
29604         }
29605     },
29606     // private
29607     onMouseUp : function(e){
29608         if(e.button == 0){
29609             this.el.removeClass("x-btn-click");
29610             Roo.get(document).un('mouseup', this.onMouseUp, this);
29611         }
29612     },
29613     // private
29614     onMenuShow : function(e){
29615         this.el.addClass("x-btn-menu-active");
29616     },
29617     // private
29618     onMenuHide : function(e){
29619         this.el.removeClass("x-btn-menu-active");
29620     }   
29621 });
29622
29623 // Private utility class used by Button
29624 Roo.ButtonToggleMgr = function(){
29625    var groups = {};
29626    
29627    function toggleGroup(btn, state){
29628        if(state){
29629            var g = groups[btn.toggleGroup];
29630            for(var i = 0, l = g.length; i < l; i++){
29631                if(g[i] != btn){
29632                    g[i].toggle(false);
29633                }
29634            }
29635        }
29636    }
29637    
29638    return {
29639        register : function(btn){
29640            if(!btn.toggleGroup){
29641                return;
29642            }
29643            var g = groups[btn.toggleGroup];
29644            if(!g){
29645                g = groups[btn.toggleGroup] = [];
29646            }
29647            g.push(btn);
29648            btn.on("toggle", toggleGroup);
29649        },
29650        
29651        unregister : function(btn){
29652            if(!btn.toggleGroup){
29653                return;
29654            }
29655            var g = groups[btn.toggleGroup];
29656            if(g){
29657                g.remove(btn);
29658                btn.un("toggle", toggleGroup);
29659            }
29660        }
29661    };
29662 }();/*
29663  * Based on:
29664  * Ext JS Library 1.1.1
29665  * Copyright(c) 2006-2007, Ext JS, LLC.
29666  *
29667  * Originally Released Under LGPL - original licence link has changed is not relivant.
29668  *
29669  * Fork - LGPL
29670  * <script type="text/javascript">
29671  */
29672  
29673 /**
29674  * @class Roo.SplitButton
29675  * @extends Roo.Button
29676  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29677  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29678  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29679  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29680  * @cfg {String} arrowTooltip The title attribute of the arrow
29681  * @constructor
29682  * Create a new menu button
29683  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29684  * @param {Object} config The config object
29685  */
29686 Roo.SplitButton = function(renderTo, config){
29687     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29688     /**
29689      * @event arrowclick
29690      * Fires when this button's arrow is clicked
29691      * @param {SplitButton} this
29692      * @param {EventObject} e The click event
29693      */
29694     this.addEvents({"arrowclick":true});
29695 };
29696
29697 Roo.extend(Roo.SplitButton, Roo.Button, {
29698     render : function(renderTo){
29699         // this is one sweet looking template!
29700         var tpl = new Roo.Template(
29701             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29702             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29703             '<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>',
29704             "</tbody></table></td><td>",
29705             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29706             '<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>',
29707             "</tbody></table></td></tr></table>"
29708         );
29709         var btn = tpl.append(renderTo, [this.text, this.type], true);
29710         var btnEl = btn.child("button");
29711         if(this.cls){
29712             btn.addClass(this.cls);
29713         }
29714         if(this.icon){
29715             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29716         }
29717         if(this.iconCls){
29718             btnEl.addClass(this.iconCls);
29719             if(!this.cls){
29720                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29721             }
29722         }
29723         this.el = btn;
29724         if(this.handleMouseEvents){
29725             btn.on("mouseover", this.onMouseOver, this);
29726             btn.on("mouseout", this.onMouseOut, this);
29727             btn.on("mousedown", this.onMouseDown, this);
29728             btn.on("mouseup", this.onMouseUp, this);
29729         }
29730         btn.on(this.clickEvent, this.onClick, this);
29731         if(this.tooltip){
29732             if(typeof this.tooltip == 'object'){
29733                 Roo.QuickTips.tips(Roo.apply({
29734                       target: btnEl.id
29735                 }, this.tooltip));
29736             } else {
29737                 btnEl.dom[this.tooltipType] = this.tooltip;
29738             }
29739         }
29740         if(this.arrowTooltip){
29741             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29742         }
29743         if(this.hidden){
29744             this.hide();
29745         }
29746         if(this.disabled){
29747             this.disable();
29748         }
29749         if(this.pressed){
29750             this.el.addClass("x-btn-pressed");
29751         }
29752         if(Roo.isIE && !Roo.isIE7){
29753             this.autoWidth.defer(1, this);
29754         }else{
29755             this.autoWidth();
29756         }
29757         if(this.menu){
29758             this.menu.on("show", this.onMenuShow, this);
29759             this.menu.on("hide", this.onMenuHide, this);
29760         }
29761         this.fireEvent('render', this);
29762     },
29763
29764     // private
29765     autoWidth : function(){
29766         if(this.el){
29767             var tbl = this.el.child("table:first");
29768             var tbl2 = this.el.child("table:last");
29769             this.el.setWidth("auto");
29770             tbl.setWidth("auto");
29771             if(Roo.isIE7 && Roo.isStrict){
29772                 var ib = this.el.child('button:first');
29773                 if(ib && ib.getWidth() > 20){
29774                     ib.clip();
29775                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29776                 }
29777             }
29778             if(this.minWidth){
29779                 if(this.hidden){
29780                     this.el.beginMeasure();
29781                 }
29782                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29783                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29784                 }
29785                 if(this.hidden){
29786                     this.el.endMeasure();
29787                 }
29788             }
29789             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29790         } 
29791     },
29792     /**
29793      * Sets this button's click handler
29794      * @param {Function} handler The function to call when the button is clicked
29795      * @param {Object} scope (optional) Scope for the function passed above
29796      */
29797     setHandler : function(handler, scope){
29798         this.handler = handler;
29799         this.scope = scope;  
29800     },
29801     
29802     /**
29803      * Sets this button's arrow click handler
29804      * @param {Function} handler The function to call when the arrow is clicked
29805      * @param {Object} scope (optional) Scope for the function passed above
29806      */
29807     setArrowHandler : function(handler, scope){
29808         this.arrowHandler = handler;
29809         this.scope = scope;  
29810     },
29811     
29812     /**
29813      * Focus the button
29814      */
29815     focus : function(){
29816         if(this.el){
29817             this.el.child("button:first").focus();
29818         }
29819     },
29820
29821     // private
29822     onClick : function(e){
29823         e.preventDefault();
29824         if(!this.disabled){
29825             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29826                 if(this.menu && !this.menu.isVisible()){
29827                     this.menu.show(this.el, this.menuAlign);
29828                 }
29829                 this.fireEvent("arrowclick", this, e);
29830                 if(this.arrowHandler){
29831                     this.arrowHandler.call(this.scope || this, this, e);
29832                 }
29833             }else{
29834                 this.fireEvent("click", this, e);
29835                 if(this.handler){
29836                     this.handler.call(this.scope || this, this, e);
29837                 }
29838             }
29839         }
29840     },
29841     // private
29842     onMouseDown : function(e){
29843         if(!this.disabled){
29844             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29845         }
29846     },
29847     // private
29848     onMouseUp : function(e){
29849         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29850     }   
29851 });
29852
29853
29854 // backwards compat
29855 Roo.MenuButton = Roo.SplitButton;/*
29856  * Based on:
29857  * Ext JS Library 1.1.1
29858  * Copyright(c) 2006-2007, Ext JS, LLC.
29859  *
29860  * Originally Released Under LGPL - original licence link has changed is not relivant.
29861  *
29862  * Fork - LGPL
29863  * <script type="text/javascript">
29864  */
29865
29866 /**
29867  * @class Roo.Toolbar
29868  * Basic Toolbar class.
29869  * @constructor
29870  * Creates a new Toolbar
29871  * @param {Object} container The config object
29872  */ 
29873 Roo.Toolbar = function(container, buttons, config)
29874 {
29875     /// old consturctor format still supported..
29876     if(container instanceof Array){ // omit the container for later rendering
29877         buttons = container;
29878         config = buttons;
29879         container = null;
29880     }
29881     if (typeof(container) == 'object' && container.xtype) {
29882         config = container;
29883         container = config.container;
29884         buttons = config.buttons || []; // not really - use items!!
29885     }
29886     var xitems = [];
29887     if (config && config.items) {
29888         xitems = config.items;
29889         delete config.items;
29890     }
29891     Roo.apply(this, config);
29892     this.buttons = buttons;
29893     
29894     if(container){
29895         this.render(container);
29896     }
29897     this.xitems = xitems;
29898     Roo.each(xitems, function(b) {
29899         this.add(b);
29900     }, this);
29901     
29902 };
29903
29904 Roo.Toolbar.prototype = {
29905     /**
29906      * @cfg {Array} items
29907      * array of button configs or elements to add (will be converted to a MixedCollection)
29908      */
29909     
29910     /**
29911      * @cfg {String/HTMLElement/Element} container
29912      * The id or element that will contain the toolbar
29913      */
29914     // private
29915     render : function(ct){
29916         this.el = Roo.get(ct);
29917         if(this.cls){
29918             this.el.addClass(this.cls);
29919         }
29920         // using a table allows for vertical alignment
29921         // 100% width is needed by Safari...
29922         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29923         this.tr = this.el.child("tr", true);
29924         var autoId = 0;
29925         this.items = new Roo.util.MixedCollection(false, function(o){
29926             return o.id || ("item" + (++autoId));
29927         });
29928         if(this.buttons){
29929             this.add.apply(this, this.buttons);
29930             delete this.buttons;
29931         }
29932     },
29933
29934     /**
29935      * Adds element(s) to the toolbar -- this function takes a variable number of 
29936      * arguments of mixed type and adds them to the toolbar.
29937      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29938      * <ul>
29939      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29940      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29941      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29942      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29943      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29944      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29945      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29946      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29947      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29948      * </ul>
29949      * @param {Mixed} arg2
29950      * @param {Mixed} etc.
29951      */
29952     add : function(){
29953         var a = arguments, l = a.length;
29954         for(var i = 0; i < l; i++){
29955             this._add(a[i]);
29956         }
29957     },
29958     // private..
29959     _add : function(el) {
29960         
29961         if (el.xtype) {
29962             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29963         }
29964         
29965         if (el.applyTo){ // some kind of form field
29966             return this.addField(el);
29967         } 
29968         if (el.render){ // some kind of Toolbar.Item
29969             return this.addItem(el);
29970         }
29971         if (typeof el == "string"){ // string
29972             if(el == "separator" || el == "-"){
29973                 return this.addSeparator();
29974             }
29975             if (el == " "){
29976                 return this.addSpacer();
29977             }
29978             if(el == "->"){
29979                 return this.addFill();
29980             }
29981             return this.addText(el);
29982             
29983         }
29984         if(el.tagName){ // element
29985             return this.addElement(el);
29986         }
29987         if(typeof el == "object"){ // must be button config?
29988             return this.addButton(el);
29989         }
29990         // and now what?!?!
29991         return false;
29992         
29993     },
29994     
29995     /**
29996      * Add an Xtype element
29997      * @param {Object} xtype Xtype Object
29998      * @return {Object} created Object
29999      */
30000     addxtype : function(e){
30001         return this.add(e);  
30002     },
30003     
30004     /**
30005      * Returns the Element for this toolbar.
30006      * @return {Roo.Element}
30007      */
30008     getEl : function(){
30009         return this.el;  
30010     },
30011     
30012     /**
30013      * Adds a separator
30014      * @return {Roo.Toolbar.Item} The separator item
30015      */
30016     addSeparator : function(){
30017         return this.addItem(new Roo.Toolbar.Separator());
30018     },
30019
30020     /**
30021      * Adds a spacer element
30022      * @return {Roo.Toolbar.Spacer} The spacer item
30023      */
30024     addSpacer : function(){
30025         return this.addItem(new Roo.Toolbar.Spacer());
30026     },
30027
30028     /**
30029      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30030      * @return {Roo.Toolbar.Fill} The fill item
30031      */
30032     addFill : function(){
30033         return this.addItem(new Roo.Toolbar.Fill());
30034     },
30035
30036     /**
30037      * Adds any standard HTML element to the toolbar
30038      * @param {String/HTMLElement/Element} el The element or id of the element to add
30039      * @return {Roo.Toolbar.Item} The element's item
30040      */
30041     addElement : function(el){
30042         return this.addItem(new Roo.Toolbar.Item(el));
30043     },
30044     /**
30045      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30046      * @type Roo.util.MixedCollection  
30047      */
30048     items : false,
30049      
30050     /**
30051      * Adds any Toolbar.Item or subclass
30052      * @param {Roo.Toolbar.Item} item
30053      * @return {Roo.Toolbar.Item} The item
30054      */
30055     addItem : function(item){
30056         var td = this.nextBlock();
30057         item.render(td);
30058         this.items.add(item);
30059         return item;
30060     },
30061     
30062     /**
30063      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30064      * @param {Object/Array} config A button config or array of configs
30065      * @return {Roo.Toolbar.Button/Array}
30066      */
30067     addButton : function(config){
30068         if(config instanceof Array){
30069             var buttons = [];
30070             for(var i = 0, len = config.length; i < len; i++) {
30071                 buttons.push(this.addButton(config[i]));
30072             }
30073             return buttons;
30074         }
30075         var b = config;
30076         if(!(config instanceof Roo.Toolbar.Button)){
30077             b = config.split ?
30078                 new Roo.Toolbar.SplitButton(config) :
30079                 new Roo.Toolbar.Button(config);
30080         }
30081         var td = this.nextBlock();
30082         b.render(td);
30083         this.items.add(b);
30084         return b;
30085     },
30086     
30087     /**
30088      * Adds text to the toolbar
30089      * @param {String} text The text to add
30090      * @return {Roo.Toolbar.Item} The element's item
30091      */
30092     addText : function(text){
30093         return this.addItem(new Roo.Toolbar.TextItem(text));
30094     },
30095     
30096     /**
30097      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30098      * @param {Number} index The index where the item is to be inserted
30099      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30100      * @return {Roo.Toolbar.Button/Item}
30101      */
30102     insertButton : function(index, item){
30103         if(item instanceof Array){
30104             var buttons = [];
30105             for(var i = 0, len = item.length; i < len; i++) {
30106                buttons.push(this.insertButton(index + i, item[i]));
30107             }
30108             return buttons;
30109         }
30110         if (!(item instanceof Roo.Toolbar.Button)){
30111            item = new Roo.Toolbar.Button(item);
30112         }
30113         var td = document.createElement("td");
30114         this.tr.insertBefore(td, this.tr.childNodes[index]);
30115         item.render(td);
30116         this.items.insert(index, item);
30117         return item;
30118     },
30119     
30120     /**
30121      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30122      * @param {Object} config
30123      * @return {Roo.Toolbar.Item} The element's item
30124      */
30125     addDom : function(config, returnEl){
30126         var td = this.nextBlock();
30127         Roo.DomHelper.overwrite(td, config);
30128         var ti = new Roo.Toolbar.Item(td.firstChild);
30129         ti.render(td);
30130         this.items.add(ti);
30131         return ti;
30132     },
30133
30134     /**
30135      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30136      * @type Roo.util.MixedCollection  
30137      */
30138     fields : false,
30139     
30140     /**
30141      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30142      * Note: the field should not have been rendered yet. For a field that has already been
30143      * rendered, use {@link #addElement}.
30144      * @param {Roo.form.Field} field
30145      * @return {Roo.ToolbarItem}
30146      */
30147      
30148       
30149     addField : function(field) {
30150         if (!this.fields) {
30151             var autoId = 0;
30152             this.fields = new Roo.util.MixedCollection(false, function(o){
30153                 return o.id || ("item" + (++autoId));
30154             });
30155
30156         }
30157         
30158         var td = this.nextBlock();
30159         field.render(td);
30160         var ti = new Roo.Toolbar.Item(td.firstChild);
30161         ti.render(td);
30162         this.items.add(ti);
30163         this.fields.add(field);
30164         return ti;
30165     },
30166     /**
30167      * Hide the toolbar
30168      * @method hide
30169      */
30170      
30171       
30172     hide : function()
30173     {
30174         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30175         this.el.child('div').hide();
30176     },
30177     /**
30178      * Show the toolbar
30179      * @method show
30180      */
30181     show : function()
30182     {
30183         this.el.child('div').show();
30184     },
30185       
30186     // private
30187     nextBlock : function(){
30188         var td = document.createElement("td");
30189         this.tr.appendChild(td);
30190         return td;
30191     },
30192
30193     // private
30194     destroy : function(){
30195         if(this.items){ // rendered?
30196             Roo.destroy.apply(Roo, this.items.items);
30197         }
30198         if(this.fields){ // rendered?
30199             Roo.destroy.apply(Roo, this.fields.items);
30200         }
30201         Roo.Element.uncache(this.el, this.tr);
30202     }
30203 };
30204
30205 /**
30206  * @class Roo.Toolbar.Item
30207  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30208  * @constructor
30209  * Creates a new Item
30210  * @param {HTMLElement} el 
30211  */
30212 Roo.Toolbar.Item = function(el){
30213     var cfg = {};
30214     if (typeof (el.xtype) != 'undefined') {
30215         cfg = el;
30216         el = cfg.el;
30217     }
30218     
30219     this.el = Roo.getDom(el);
30220     this.id = Roo.id(this.el);
30221     this.hidden = false;
30222     
30223     this.addEvents({
30224          /**
30225              * @event render
30226              * Fires when the button is rendered
30227              * @param {Button} this
30228              */
30229         'render': true
30230     });
30231     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30232 };
30233 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30234 //Roo.Toolbar.Item.prototype = {
30235     
30236     /**
30237      * Get this item's HTML Element
30238      * @return {HTMLElement}
30239      */
30240     getEl : function(){
30241        return this.el;  
30242     },
30243
30244     // private
30245     render : function(td){
30246         
30247          this.td = td;
30248         td.appendChild(this.el);
30249         
30250         this.fireEvent('render', this);
30251     },
30252     
30253     /**
30254      * Removes and destroys this item.
30255      */
30256     destroy : function(){
30257         this.td.parentNode.removeChild(this.td);
30258     },
30259     
30260     /**
30261      * Shows this item.
30262      */
30263     show: function(){
30264         this.hidden = false;
30265         this.td.style.display = "";
30266     },
30267     
30268     /**
30269      * Hides this item.
30270      */
30271     hide: function(){
30272         this.hidden = true;
30273         this.td.style.display = "none";
30274     },
30275     
30276     /**
30277      * Convenience function for boolean show/hide.
30278      * @param {Boolean} visible true to show/false to hide
30279      */
30280     setVisible: function(visible){
30281         if(visible) {
30282             this.show();
30283         }else{
30284             this.hide();
30285         }
30286     },
30287     
30288     /**
30289      * Try to focus this item.
30290      */
30291     focus : function(){
30292         Roo.fly(this.el).focus();
30293     },
30294     
30295     /**
30296      * Disables this item.
30297      */
30298     disable : function(){
30299         Roo.fly(this.td).addClass("x-item-disabled");
30300         this.disabled = true;
30301         this.el.disabled = true;
30302     },
30303     
30304     /**
30305      * Enables this item.
30306      */
30307     enable : function(){
30308         Roo.fly(this.td).removeClass("x-item-disabled");
30309         this.disabled = false;
30310         this.el.disabled = false;
30311     }
30312 });
30313
30314
30315 /**
30316  * @class Roo.Toolbar.Separator
30317  * @extends Roo.Toolbar.Item
30318  * A simple toolbar separator class
30319  * @constructor
30320  * Creates a new Separator
30321  */
30322 Roo.Toolbar.Separator = function(cfg){
30323     
30324     var s = document.createElement("span");
30325     s.className = "ytb-sep";
30326     if (cfg) {
30327         cfg.el = s;
30328     }
30329     
30330     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30331 };
30332 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30333     enable:Roo.emptyFn,
30334     disable:Roo.emptyFn,
30335     focus:Roo.emptyFn
30336 });
30337
30338 /**
30339  * @class Roo.Toolbar.Spacer
30340  * @extends Roo.Toolbar.Item
30341  * A simple element that adds extra horizontal space to a toolbar.
30342  * @constructor
30343  * Creates a new Spacer
30344  */
30345 Roo.Toolbar.Spacer = function(cfg){
30346     var s = document.createElement("div");
30347     s.className = "ytb-spacer";
30348     if (cfg) {
30349         cfg.el = s;
30350     }
30351     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30352 };
30353 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30354     enable:Roo.emptyFn,
30355     disable:Roo.emptyFn,
30356     focus:Roo.emptyFn
30357 });
30358
30359 /**
30360  * @class Roo.Toolbar.Fill
30361  * @extends Roo.Toolbar.Spacer
30362  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30363  * @constructor
30364  * Creates a new Spacer
30365  */
30366 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30367     // private
30368     render : function(td){
30369         td.style.width = '100%';
30370         Roo.Toolbar.Fill.superclass.render.call(this, td);
30371     }
30372 });
30373
30374 /**
30375  * @class Roo.Toolbar.TextItem
30376  * @extends Roo.Toolbar.Item
30377  * A simple class that renders text directly into a toolbar.
30378  * @constructor
30379  * Creates a new TextItem
30380  * @param {String} text
30381  */
30382 Roo.Toolbar.TextItem = function(cfg){
30383     var  text = cfg || "";
30384     if (typeof(cfg) == 'object') {
30385         text = cfg.text || "";
30386     }  else {
30387         cfg = null;
30388     }
30389     var s = document.createElement("span");
30390     s.className = "ytb-text";
30391     s.innerHTML = text;
30392     if (cfg) {
30393         cfg.el  = s;
30394     }
30395     
30396     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30397 };
30398 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30399     
30400      
30401     enable:Roo.emptyFn,
30402     disable:Roo.emptyFn,
30403     focus:Roo.emptyFn
30404 });
30405
30406 /**
30407  * @class Roo.Toolbar.Button
30408  * @extends Roo.Button
30409  * A button that renders into a toolbar.
30410  * @constructor
30411  * Creates a new Button
30412  * @param {Object} config A standard {@link Roo.Button} config object
30413  */
30414 Roo.Toolbar.Button = function(config){
30415     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30416 };
30417 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30418     render : function(td){
30419         this.td = td;
30420         Roo.Toolbar.Button.superclass.render.call(this, td);
30421     },
30422     
30423     /**
30424      * Removes and destroys this button
30425      */
30426     destroy : function(){
30427         Roo.Toolbar.Button.superclass.destroy.call(this);
30428         this.td.parentNode.removeChild(this.td);
30429     },
30430     
30431     /**
30432      * Shows this button
30433      */
30434     show: function(){
30435         this.hidden = false;
30436         this.td.style.display = "";
30437     },
30438     
30439     /**
30440      * Hides this button
30441      */
30442     hide: function(){
30443         this.hidden = true;
30444         this.td.style.display = "none";
30445     },
30446
30447     /**
30448      * Disables this item
30449      */
30450     disable : function(){
30451         Roo.fly(this.td).addClass("x-item-disabled");
30452         this.disabled = true;
30453     },
30454
30455     /**
30456      * Enables this item
30457      */
30458     enable : function(){
30459         Roo.fly(this.td).removeClass("x-item-disabled");
30460         this.disabled = false;
30461     }
30462 });
30463 // backwards compat
30464 Roo.ToolbarButton = Roo.Toolbar.Button;
30465
30466 /**
30467  * @class Roo.Toolbar.SplitButton
30468  * @extends Roo.SplitButton
30469  * A menu button that renders into a toolbar.
30470  * @constructor
30471  * Creates a new SplitButton
30472  * @param {Object} config A standard {@link Roo.SplitButton} config object
30473  */
30474 Roo.Toolbar.SplitButton = function(config){
30475     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30476 };
30477 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30478     render : function(td){
30479         this.td = td;
30480         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30481     },
30482     
30483     /**
30484      * Removes and destroys this button
30485      */
30486     destroy : function(){
30487         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30488         this.td.parentNode.removeChild(this.td);
30489     },
30490     
30491     /**
30492      * Shows this button
30493      */
30494     show: function(){
30495         this.hidden = false;
30496         this.td.style.display = "";
30497     },
30498     
30499     /**
30500      * Hides this button
30501      */
30502     hide: function(){
30503         this.hidden = true;
30504         this.td.style.display = "none";
30505     }
30506 });
30507
30508 // backwards compat
30509 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30510  * Based on:
30511  * Ext JS Library 1.1.1
30512  * Copyright(c) 2006-2007, Ext JS, LLC.
30513  *
30514  * Originally Released Under LGPL - original licence link has changed is not relivant.
30515  *
30516  * Fork - LGPL
30517  * <script type="text/javascript">
30518  */
30519  
30520 /**
30521  * @class Roo.PagingToolbar
30522  * @extends Roo.Toolbar
30523  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30524  * @constructor
30525  * Create a new PagingToolbar
30526  * @param {Object} config The config object
30527  */
30528 Roo.PagingToolbar = function(el, ds, config)
30529 {
30530     // old args format still supported... - xtype is prefered..
30531     if (typeof(el) == 'object' && el.xtype) {
30532         // created from xtype...
30533         config = el;
30534         ds = el.dataSource;
30535         el = config.container;
30536     }
30537     var items = [];
30538     if (config.items) {
30539         items = config.items;
30540         config.items = [];
30541     }
30542     
30543     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30544     this.ds = ds;
30545     this.cursor = 0;
30546     this.renderButtons(this.el);
30547     this.bind(ds);
30548     
30549     // supprot items array.
30550    
30551     Roo.each(items, function(e) {
30552         this.add(Roo.factory(e));
30553     },this);
30554     
30555 };
30556
30557 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30558     /**
30559      * @cfg {Roo.data.Store} dataSource
30560      * The underlying data store providing the paged data
30561      */
30562     /**
30563      * @cfg {String/HTMLElement/Element} container
30564      * container The id or element that will contain the toolbar
30565      */
30566     /**
30567      * @cfg {Boolean} displayInfo
30568      * True to display the displayMsg (defaults to false)
30569      */
30570     /**
30571      * @cfg {Number} pageSize
30572      * The number of records to display per page (defaults to 20)
30573      */
30574     pageSize: 20,
30575     /**
30576      * @cfg {String} displayMsg
30577      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30578      */
30579     displayMsg : 'Displaying {0} - {1} of {2}',
30580     /**
30581      * @cfg {String} emptyMsg
30582      * The message to display when no records are found (defaults to "No data to display")
30583      */
30584     emptyMsg : 'No data to display',
30585     /**
30586      * Customizable piece of the default paging text (defaults to "Page")
30587      * @type String
30588      */
30589     beforePageText : "Page",
30590     /**
30591      * Customizable piece of the default paging text (defaults to "of %0")
30592      * @type String
30593      */
30594     afterPageText : "of {0}",
30595     /**
30596      * Customizable piece of the default paging text (defaults to "First Page")
30597      * @type String
30598      */
30599     firstText : "First Page",
30600     /**
30601      * Customizable piece of the default paging text (defaults to "Previous Page")
30602      * @type String
30603      */
30604     prevText : "Previous Page",
30605     /**
30606      * Customizable piece of the default paging text (defaults to "Next Page")
30607      * @type String
30608      */
30609     nextText : "Next Page",
30610     /**
30611      * Customizable piece of the default paging text (defaults to "Last Page")
30612      * @type String
30613      */
30614     lastText : "Last Page",
30615     /**
30616      * Customizable piece of the default paging text (defaults to "Refresh")
30617      * @type String
30618      */
30619     refreshText : "Refresh",
30620
30621     // private
30622     renderButtons : function(el){
30623         Roo.PagingToolbar.superclass.render.call(this, el);
30624         this.first = this.addButton({
30625             tooltip: this.firstText,
30626             cls: "x-btn-icon x-grid-page-first",
30627             disabled: true,
30628             handler: this.onClick.createDelegate(this, ["first"])
30629         });
30630         this.prev = this.addButton({
30631             tooltip: this.prevText,
30632             cls: "x-btn-icon x-grid-page-prev",
30633             disabled: true,
30634             handler: this.onClick.createDelegate(this, ["prev"])
30635         });
30636         //this.addSeparator();
30637         this.add(this.beforePageText);
30638         this.field = Roo.get(this.addDom({
30639            tag: "input",
30640            type: "text",
30641            size: "3",
30642            value: "1",
30643            cls: "x-grid-page-number"
30644         }).el);
30645         this.field.on("keydown", this.onPagingKeydown, this);
30646         this.field.on("focus", function(){this.dom.select();});
30647         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30648         this.field.setHeight(18);
30649         //this.addSeparator();
30650         this.next = this.addButton({
30651             tooltip: this.nextText,
30652             cls: "x-btn-icon x-grid-page-next",
30653             disabled: true,
30654             handler: this.onClick.createDelegate(this, ["next"])
30655         });
30656         this.last = this.addButton({
30657             tooltip: this.lastText,
30658             cls: "x-btn-icon x-grid-page-last",
30659             disabled: true,
30660             handler: this.onClick.createDelegate(this, ["last"])
30661         });
30662         //this.addSeparator();
30663         this.loading = this.addButton({
30664             tooltip: this.refreshText,
30665             cls: "x-btn-icon x-grid-loading",
30666             handler: this.onClick.createDelegate(this, ["refresh"])
30667         });
30668
30669         if(this.displayInfo){
30670             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30671         }
30672     },
30673
30674     // private
30675     updateInfo : function(){
30676         if(this.displayEl){
30677             var count = this.ds.getCount();
30678             var msg = count == 0 ?
30679                 this.emptyMsg :
30680                 String.format(
30681                     this.displayMsg,
30682                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30683                 );
30684             this.displayEl.update(msg);
30685         }
30686     },
30687
30688     // private
30689     onLoad : function(ds, r, o){
30690        this.cursor = o.params ? o.params.start : 0;
30691        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30692
30693        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30694        this.field.dom.value = ap;
30695        this.first.setDisabled(ap == 1);
30696        this.prev.setDisabled(ap == 1);
30697        this.next.setDisabled(ap == ps);
30698        this.last.setDisabled(ap == ps);
30699        this.loading.enable();
30700        this.updateInfo();
30701     },
30702
30703     // private
30704     getPageData : function(){
30705         var total = this.ds.getTotalCount();
30706         return {
30707             total : total,
30708             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30709             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30710         };
30711     },
30712
30713     // private
30714     onLoadError : function(){
30715         this.loading.enable();
30716     },
30717
30718     // private
30719     onPagingKeydown : function(e){
30720         var k = e.getKey();
30721         var d = this.getPageData();
30722         if(k == e.RETURN){
30723             var v = this.field.dom.value, pageNum;
30724             if(!v || isNaN(pageNum = parseInt(v, 10))){
30725                 this.field.dom.value = d.activePage;
30726                 return;
30727             }
30728             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30729             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30730             e.stopEvent();
30731         }
30732         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))
30733         {
30734           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30735           this.field.dom.value = pageNum;
30736           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30737           e.stopEvent();
30738         }
30739         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30740         {
30741           var v = this.field.dom.value, pageNum; 
30742           var increment = (e.shiftKey) ? 10 : 1;
30743           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30744             increment *= -1;
30745           }
30746           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30747             this.field.dom.value = d.activePage;
30748             return;
30749           }
30750           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30751           {
30752             this.field.dom.value = parseInt(v, 10) + increment;
30753             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30754             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30755           }
30756           e.stopEvent();
30757         }
30758     },
30759
30760     // private
30761     beforeLoad : function(){
30762         if(this.loading){
30763             this.loading.disable();
30764         }
30765     },
30766
30767     // private
30768     onClick : function(which){
30769         var ds = this.ds;
30770         switch(which){
30771             case "first":
30772                 ds.load({params:{start: 0, limit: this.pageSize}});
30773             break;
30774             case "prev":
30775                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30776             break;
30777             case "next":
30778                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30779             break;
30780             case "last":
30781                 var total = ds.getTotalCount();
30782                 var extra = total % this.pageSize;
30783                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30784                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30785             break;
30786             case "refresh":
30787                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30788             break;
30789         }
30790     },
30791
30792     /**
30793      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30794      * @param {Roo.data.Store} store The data store to unbind
30795      */
30796     unbind : function(ds){
30797         ds.un("beforeload", this.beforeLoad, this);
30798         ds.un("load", this.onLoad, this);
30799         ds.un("loadexception", this.onLoadError, this);
30800         ds.un("remove", this.updateInfo, this);
30801         ds.un("add", this.updateInfo, this);
30802         this.ds = undefined;
30803     },
30804
30805     /**
30806      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30807      * @param {Roo.data.Store} store The data store to bind
30808      */
30809     bind : function(ds){
30810         ds.on("beforeload", this.beforeLoad, this);
30811         ds.on("load", this.onLoad, this);
30812         ds.on("loadexception", this.onLoadError, this);
30813         ds.on("remove", this.updateInfo, this);
30814         ds.on("add", this.updateInfo, this);
30815         this.ds = ds;
30816     }
30817 });/*
30818  * Based on:
30819  * Ext JS Library 1.1.1
30820  * Copyright(c) 2006-2007, Ext JS, LLC.
30821  *
30822  * Originally Released Under LGPL - original licence link has changed is not relivant.
30823  *
30824  * Fork - LGPL
30825  * <script type="text/javascript">
30826  */
30827
30828 /**
30829  * @class Roo.Resizable
30830  * @extends Roo.util.Observable
30831  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30832  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30833  * 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
30834  * the element will be wrapped for you automatically.</p>
30835  * <p>Here is the list of valid resize handles:</p>
30836  * <pre>
30837 Value   Description
30838 ------  -------------------
30839  'n'     north
30840  's'     south
30841  'e'     east
30842  'w'     west
30843  'nw'    northwest
30844  'sw'    southwest
30845  'se'    southeast
30846  'ne'    northeast
30847  'hd'    horizontal drag
30848  'all'   all
30849 </pre>
30850  * <p>Here's an example showing the creation of a typical Resizable:</p>
30851  * <pre><code>
30852 var resizer = new Roo.Resizable("element-id", {
30853     handles: 'all',
30854     minWidth: 200,
30855     minHeight: 100,
30856     maxWidth: 500,
30857     maxHeight: 400,
30858     pinned: true
30859 });
30860 resizer.on("resize", myHandler);
30861 </code></pre>
30862  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30863  * resizer.east.setDisplayed(false);</p>
30864  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30865  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30866  * resize operation's new size (defaults to [0, 0])
30867  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30868  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30869  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30870  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30871  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30872  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30873  * @cfg {Number} width The width of the element in pixels (defaults to null)
30874  * @cfg {Number} height The height of the element in pixels (defaults to null)
30875  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30876  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30877  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30878  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30879  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30880  * in favor of the handles config option (defaults to false)
30881  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30882  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30883  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30884  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30885  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30886  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30887  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30888  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30889  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30890  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30891  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30892  * @constructor
30893  * Create a new resizable component
30894  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30895  * @param {Object} config configuration options
30896   */
30897 Roo.Resizable = function(el, config)
30898 {
30899     this.el = Roo.get(el);
30900
30901     if(config && config.wrap){
30902         config.resizeChild = this.el;
30903         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30904         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30905         this.el.setStyle("overflow", "hidden");
30906         this.el.setPositioning(config.resizeChild.getPositioning());
30907         config.resizeChild.clearPositioning();
30908         if(!config.width || !config.height){
30909             var csize = config.resizeChild.getSize();
30910             this.el.setSize(csize.width, csize.height);
30911         }
30912         if(config.pinned && !config.adjustments){
30913             config.adjustments = "auto";
30914         }
30915     }
30916
30917     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30918     this.proxy.unselectable();
30919     this.proxy.enableDisplayMode('block');
30920
30921     Roo.apply(this, config);
30922
30923     if(this.pinned){
30924         this.disableTrackOver = true;
30925         this.el.addClass("x-resizable-pinned");
30926     }
30927     // if the element isn't positioned, make it relative
30928     var position = this.el.getStyle("position");
30929     if(position != "absolute" && position != "fixed"){
30930         this.el.setStyle("position", "relative");
30931     }
30932     if(!this.handles){ // no handles passed, must be legacy style
30933         this.handles = 's,e,se';
30934         if(this.multiDirectional){
30935             this.handles += ',n,w';
30936         }
30937     }
30938     if(this.handles == "all"){
30939         this.handles = "n s e w ne nw se sw";
30940     }
30941     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30942     var ps = Roo.Resizable.positions;
30943     for(var i = 0, len = hs.length; i < len; i++){
30944         if(hs[i] && ps[hs[i]]){
30945             var pos = ps[hs[i]];
30946             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30947         }
30948     }
30949     // legacy
30950     this.corner = this.southeast;
30951     
30952     // updateBox = the box can move..
30953     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30954         this.updateBox = true;
30955     }
30956
30957     this.activeHandle = null;
30958
30959     if(this.resizeChild){
30960         if(typeof this.resizeChild == "boolean"){
30961             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30962         }else{
30963             this.resizeChild = Roo.get(this.resizeChild, true);
30964         }
30965     }
30966     
30967     if(this.adjustments == "auto"){
30968         var rc = this.resizeChild;
30969         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30970         if(rc && (hw || hn)){
30971             rc.position("relative");
30972             rc.setLeft(hw ? hw.el.getWidth() : 0);
30973             rc.setTop(hn ? hn.el.getHeight() : 0);
30974         }
30975         this.adjustments = [
30976             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30977             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30978         ];
30979     }
30980
30981     if(this.draggable){
30982         this.dd = this.dynamic ?
30983             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30984         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30985     }
30986
30987     // public events
30988     this.addEvents({
30989         /**
30990          * @event beforeresize
30991          * Fired before resize is allowed. Set enabled to false to cancel resize.
30992          * @param {Roo.Resizable} this
30993          * @param {Roo.EventObject} e The mousedown event
30994          */
30995         "beforeresize" : true,
30996         /**
30997          * @event resizing
30998          * Fired a resizing.
30999          * @param {Roo.Resizable} this
31000          * @param {Number} x The new x position
31001          * @param {Number} y The new y position
31002          * @param {Number} w The new w width
31003          * @param {Number} h The new h hight
31004          * @param {Roo.EventObject} e The mouseup event
31005          */
31006         "resizing" : true,
31007         /**
31008          * @event resize
31009          * Fired after a resize.
31010          * @param {Roo.Resizable} this
31011          * @param {Number} width The new width
31012          * @param {Number} height The new height
31013          * @param {Roo.EventObject} e The mouseup event
31014          */
31015         "resize" : true
31016     });
31017
31018     if(this.width !== null && this.height !== null){
31019         this.resizeTo(this.width, this.height);
31020     }else{
31021         this.updateChildSize();
31022     }
31023     if(Roo.isIE){
31024         this.el.dom.style.zoom = 1;
31025     }
31026     Roo.Resizable.superclass.constructor.call(this);
31027 };
31028
31029 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31030         resizeChild : false,
31031         adjustments : [0, 0],
31032         minWidth : 5,
31033         minHeight : 5,
31034         maxWidth : 10000,
31035         maxHeight : 10000,
31036         enabled : true,
31037         animate : false,
31038         duration : .35,
31039         dynamic : false,
31040         handles : false,
31041         multiDirectional : false,
31042         disableTrackOver : false,
31043         easing : 'easeOutStrong',
31044         widthIncrement : 0,
31045         heightIncrement : 0,
31046         pinned : false,
31047         width : null,
31048         height : null,
31049         preserveRatio : false,
31050         transparent: false,
31051         minX: 0,
31052         minY: 0,
31053         draggable: false,
31054
31055         /**
31056          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31057          */
31058         constrainTo: undefined,
31059         /**
31060          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31061          */
31062         resizeRegion: undefined,
31063
31064
31065     /**
31066      * Perform a manual resize
31067      * @param {Number} width
31068      * @param {Number} height
31069      */
31070     resizeTo : function(width, height){
31071         this.el.setSize(width, height);
31072         this.updateChildSize();
31073         this.fireEvent("resize", this, width, height, null);
31074     },
31075
31076     // private
31077     startSizing : function(e, handle){
31078         this.fireEvent("beforeresize", this, e);
31079         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31080
31081             if(!this.overlay){
31082                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31083                 this.overlay.unselectable();
31084                 this.overlay.enableDisplayMode("block");
31085                 this.overlay.on("mousemove", this.onMouseMove, this);
31086                 this.overlay.on("mouseup", this.onMouseUp, this);
31087             }
31088             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31089
31090             this.resizing = true;
31091             this.startBox = this.el.getBox();
31092             this.startPoint = e.getXY();
31093             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31094                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31095
31096             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31097             this.overlay.show();
31098
31099             if(this.constrainTo) {
31100                 var ct = Roo.get(this.constrainTo);
31101                 this.resizeRegion = ct.getRegion().adjust(
31102                     ct.getFrameWidth('t'),
31103                     ct.getFrameWidth('l'),
31104                     -ct.getFrameWidth('b'),
31105                     -ct.getFrameWidth('r')
31106                 );
31107             }
31108
31109             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31110             this.proxy.show();
31111             this.proxy.setBox(this.startBox);
31112             if(!this.dynamic){
31113                 this.proxy.setStyle('visibility', 'visible');
31114             }
31115         }
31116     },
31117
31118     // private
31119     onMouseDown : function(handle, e){
31120         if(this.enabled){
31121             e.stopEvent();
31122             this.activeHandle = handle;
31123             this.startSizing(e, handle);
31124         }
31125     },
31126
31127     // private
31128     onMouseUp : function(e){
31129         var size = this.resizeElement();
31130         this.resizing = false;
31131         this.handleOut();
31132         this.overlay.hide();
31133         this.proxy.hide();
31134         this.fireEvent("resize", this, size.width, size.height, e);
31135     },
31136
31137     // private
31138     updateChildSize : function(){
31139         
31140         if(this.resizeChild){
31141             var el = this.el;
31142             var child = this.resizeChild;
31143             var adj = this.adjustments;
31144             if(el.dom.offsetWidth){
31145                 var b = el.getSize(true);
31146                 child.setSize(b.width+adj[0], b.height+adj[1]);
31147             }
31148             // Second call here for IE
31149             // The first call enables instant resizing and
31150             // the second call corrects scroll bars if they
31151             // exist
31152             if(Roo.isIE){
31153                 setTimeout(function(){
31154                     if(el.dom.offsetWidth){
31155                         var b = el.getSize(true);
31156                         child.setSize(b.width+adj[0], b.height+adj[1]);
31157                     }
31158                 }, 10);
31159             }
31160         }
31161     },
31162
31163     // private
31164     snap : function(value, inc, min){
31165         if(!inc || !value) {
31166             return value;
31167         }
31168         var newValue = value;
31169         var m = value % inc;
31170         if(m > 0){
31171             if(m > (inc/2)){
31172                 newValue = value + (inc-m);
31173             }else{
31174                 newValue = value - m;
31175             }
31176         }
31177         return Math.max(min, newValue);
31178     },
31179
31180     // private
31181     resizeElement : function(){
31182         var box = this.proxy.getBox();
31183         if(this.updateBox){
31184             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31185         }else{
31186             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31187         }
31188         this.updateChildSize();
31189         if(!this.dynamic){
31190             this.proxy.hide();
31191         }
31192         return box;
31193     },
31194
31195     // private
31196     constrain : function(v, diff, m, mx){
31197         if(v - diff < m){
31198             diff = v - m;
31199         }else if(v - diff > mx){
31200             diff = mx - v;
31201         }
31202         return diff;
31203     },
31204
31205     // private
31206     onMouseMove : function(e){
31207         
31208         if(this.enabled){
31209             try{// try catch so if something goes wrong the user doesn't get hung
31210
31211             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31212                 return;
31213             }
31214
31215             //var curXY = this.startPoint;
31216             var curSize = this.curSize || this.startBox;
31217             var x = this.startBox.x, y = this.startBox.y;
31218             var ox = x, oy = y;
31219             var w = curSize.width, h = curSize.height;
31220             var ow = w, oh = h;
31221             var mw = this.minWidth, mh = this.minHeight;
31222             var mxw = this.maxWidth, mxh = this.maxHeight;
31223             var wi = this.widthIncrement;
31224             var hi = this.heightIncrement;
31225
31226             var eventXY = e.getXY();
31227             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31228             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31229
31230             var pos = this.activeHandle.position;
31231
31232             switch(pos){
31233                 case "east":
31234                     w += diffX;
31235                     w = Math.min(Math.max(mw, w), mxw);
31236                     break;
31237              
31238                 case "south":
31239                     h += diffY;
31240                     h = Math.min(Math.max(mh, h), mxh);
31241                     break;
31242                 case "southeast":
31243                     w += diffX;
31244                     h += diffY;
31245                     w = Math.min(Math.max(mw, w), mxw);
31246                     h = Math.min(Math.max(mh, h), mxh);
31247                     break;
31248                 case "north":
31249                     diffY = this.constrain(h, diffY, mh, mxh);
31250                     y += diffY;
31251                     h -= diffY;
31252                     break;
31253                 case "hdrag":
31254                     
31255                     if (wi) {
31256                         var adiffX = Math.abs(diffX);
31257                         var sub = (adiffX % wi); // how much 
31258                         if (sub > (wi/2)) { // far enough to snap
31259                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31260                         } else {
31261                             // remove difference.. 
31262                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31263                         }
31264                     }
31265                     x += diffX;
31266                     x = Math.max(this.minX, x);
31267                     break;
31268                 case "west":
31269                     diffX = this.constrain(w, diffX, mw, mxw);
31270                     x += diffX;
31271                     w -= diffX;
31272                     break;
31273                 case "northeast":
31274                     w += diffX;
31275                     w = Math.min(Math.max(mw, w), mxw);
31276                     diffY = this.constrain(h, diffY, mh, mxh);
31277                     y += diffY;
31278                     h -= diffY;
31279                     break;
31280                 case "northwest":
31281                     diffX = this.constrain(w, diffX, mw, mxw);
31282                     diffY = this.constrain(h, diffY, mh, mxh);
31283                     y += diffY;
31284                     h -= diffY;
31285                     x += diffX;
31286                     w -= diffX;
31287                     break;
31288                case "southwest":
31289                     diffX = this.constrain(w, diffX, mw, mxw);
31290                     h += diffY;
31291                     h = Math.min(Math.max(mh, h), mxh);
31292                     x += diffX;
31293                     w -= diffX;
31294                     break;
31295             }
31296
31297             var sw = this.snap(w, wi, mw);
31298             var sh = this.snap(h, hi, mh);
31299             if(sw != w || sh != h){
31300                 switch(pos){
31301                     case "northeast":
31302                         y -= sh - h;
31303                     break;
31304                     case "north":
31305                         y -= sh - h;
31306                         break;
31307                     case "southwest":
31308                         x -= sw - w;
31309                     break;
31310                     case "west":
31311                         x -= sw - w;
31312                         break;
31313                     case "northwest":
31314                         x -= sw - w;
31315                         y -= sh - h;
31316                     break;
31317                 }
31318                 w = sw;
31319                 h = sh;
31320             }
31321
31322             if(this.preserveRatio){
31323                 switch(pos){
31324                     case "southeast":
31325                     case "east":
31326                         h = oh * (w/ow);
31327                         h = Math.min(Math.max(mh, h), mxh);
31328                         w = ow * (h/oh);
31329                        break;
31330                     case "south":
31331                         w = ow * (h/oh);
31332                         w = Math.min(Math.max(mw, w), mxw);
31333                         h = oh * (w/ow);
31334                         break;
31335                     case "northeast":
31336                         w = ow * (h/oh);
31337                         w = Math.min(Math.max(mw, w), mxw);
31338                         h = oh * (w/ow);
31339                     break;
31340                     case "north":
31341                         var tw = w;
31342                         w = ow * (h/oh);
31343                         w = Math.min(Math.max(mw, w), mxw);
31344                         h = oh * (w/ow);
31345                         x += (tw - w) / 2;
31346                         break;
31347                     case "southwest":
31348                         h = oh * (w/ow);
31349                         h = Math.min(Math.max(mh, h), mxh);
31350                         var tw = w;
31351                         w = ow * (h/oh);
31352                         x += tw - w;
31353                         break;
31354                     case "west":
31355                         var th = h;
31356                         h = oh * (w/ow);
31357                         h = Math.min(Math.max(mh, h), mxh);
31358                         y += (th - h) / 2;
31359                         var tw = w;
31360                         w = ow * (h/oh);
31361                         x += tw - w;
31362                        break;
31363                     case "northwest":
31364                         var tw = w;
31365                         var th = h;
31366                         h = oh * (w/ow);
31367                         h = Math.min(Math.max(mh, h), mxh);
31368                         w = ow * (h/oh);
31369                         y += th - h;
31370                         x += tw - w;
31371                        break;
31372
31373                 }
31374             }
31375             if (pos == 'hdrag') {
31376                 w = ow;
31377             }
31378             this.proxy.setBounds(x, y, w, h);
31379             if(this.dynamic){
31380                 this.resizeElement();
31381             }
31382             }catch(e){}
31383         }
31384         this.fireEvent("resizing", this, x, y, w, h, e);
31385     },
31386
31387     // private
31388     handleOver : function(){
31389         if(this.enabled){
31390             this.el.addClass("x-resizable-over");
31391         }
31392     },
31393
31394     // private
31395     handleOut : function(){
31396         if(!this.resizing){
31397             this.el.removeClass("x-resizable-over");
31398         }
31399     },
31400
31401     /**
31402      * Returns the element this component is bound to.
31403      * @return {Roo.Element}
31404      */
31405     getEl : function(){
31406         return this.el;
31407     },
31408
31409     /**
31410      * Returns the resizeChild element (or null).
31411      * @return {Roo.Element}
31412      */
31413     getResizeChild : function(){
31414         return this.resizeChild;
31415     },
31416     groupHandler : function()
31417     {
31418         
31419     },
31420     /**
31421      * Destroys this resizable. If the element was wrapped and
31422      * removeEl is not true then the element remains.
31423      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31424      */
31425     destroy : function(removeEl){
31426         this.proxy.remove();
31427         if(this.overlay){
31428             this.overlay.removeAllListeners();
31429             this.overlay.remove();
31430         }
31431         var ps = Roo.Resizable.positions;
31432         for(var k in ps){
31433             if(typeof ps[k] != "function" && this[ps[k]]){
31434                 var h = this[ps[k]];
31435                 h.el.removeAllListeners();
31436                 h.el.remove();
31437             }
31438         }
31439         if(removeEl){
31440             this.el.update("");
31441             this.el.remove();
31442         }
31443     }
31444 });
31445
31446 // private
31447 // hash to map config positions to true positions
31448 Roo.Resizable.positions = {
31449     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31450     hd: "hdrag"
31451 };
31452
31453 // private
31454 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31455     if(!this.tpl){
31456         // only initialize the template if resizable is used
31457         var tpl = Roo.DomHelper.createTemplate(
31458             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31459         );
31460         tpl.compile();
31461         Roo.Resizable.Handle.prototype.tpl = tpl;
31462     }
31463     this.position = pos;
31464     this.rz = rz;
31465     // show north drag fro topdra
31466     var handlepos = pos == 'hdrag' ? 'north' : pos;
31467     
31468     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31469     if (pos == 'hdrag') {
31470         this.el.setStyle('cursor', 'pointer');
31471     }
31472     this.el.unselectable();
31473     if(transparent){
31474         this.el.setOpacity(0);
31475     }
31476     this.el.on("mousedown", this.onMouseDown, this);
31477     if(!disableTrackOver){
31478         this.el.on("mouseover", this.onMouseOver, this);
31479         this.el.on("mouseout", this.onMouseOut, this);
31480     }
31481 };
31482
31483 // private
31484 Roo.Resizable.Handle.prototype = {
31485     afterResize : function(rz){
31486         Roo.log('after?');
31487         // do nothing
31488     },
31489     // private
31490     onMouseDown : function(e){
31491         this.rz.onMouseDown(this, e);
31492     },
31493     // private
31494     onMouseOver : function(e){
31495         this.rz.handleOver(this, e);
31496     },
31497     // private
31498     onMouseOut : function(e){
31499         this.rz.handleOut(this, e);
31500     }
31501 };/*
31502  * Based on:
31503  * Ext JS Library 1.1.1
31504  * Copyright(c) 2006-2007, Ext JS, LLC.
31505  *
31506  * Originally Released Under LGPL - original licence link has changed is not relivant.
31507  *
31508  * Fork - LGPL
31509  * <script type="text/javascript">
31510  */
31511
31512 /**
31513  * @class Roo.Editor
31514  * @extends Roo.Component
31515  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31516  * @constructor
31517  * Create a new Editor
31518  * @param {Roo.form.Field} field The Field object (or descendant)
31519  * @param {Object} config The config object
31520  */
31521 Roo.Editor = function(field, config){
31522     Roo.Editor.superclass.constructor.call(this, config);
31523     this.field = field;
31524     this.addEvents({
31525         /**
31526              * @event beforestartedit
31527              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31528              * false from the handler of this event.
31529              * @param {Editor} this
31530              * @param {Roo.Element} boundEl The underlying element bound to this editor
31531              * @param {Mixed} value The field value being set
31532              */
31533         "beforestartedit" : true,
31534         /**
31535              * @event startedit
31536              * Fires when this editor is displayed
31537              * @param {Roo.Element} boundEl The underlying element bound to this editor
31538              * @param {Mixed} value The starting field value
31539              */
31540         "startedit" : true,
31541         /**
31542              * @event beforecomplete
31543              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31544              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31545              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31546              * event will not fire since no edit actually occurred.
31547              * @param {Editor} this
31548              * @param {Mixed} value The current field value
31549              * @param {Mixed} startValue The original field value
31550              */
31551         "beforecomplete" : true,
31552         /**
31553              * @event complete
31554              * Fires after editing is complete and any changed value has been written to the underlying field.
31555              * @param {Editor} this
31556              * @param {Mixed} value The current field value
31557              * @param {Mixed} startValue The original field value
31558              */
31559         "complete" : true,
31560         /**
31561          * @event specialkey
31562          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31563          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31564          * @param {Roo.form.Field} this
31565          * @param {Roo.EventObject} e The event object
31566          */
31567         "specialkey" : true
31568     });
31569 };
31570
31571 Roo.extend(Roo.Editor, Roo.Component, {
31572     /**
31573      * @cfg {Boolean/String} autosize
31574      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31575      * or "height" to adopt the height only (defaults to false)
31576      */
31577     /**
31578      * @cfg {Boolean} revertInvalid
31579      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31580      * validation fails (defaults to true)
31581      */
31582     /**
31583      * @cfg {Boolean} ignoreNoChange
31584      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31585      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31586      * will never be ignored.
31587      */
31588     /**
31589      * @cfg {Boolean} hideEl
31590      * False to keep the bound element visible while the editor is displayed (defaults to true)
31591      */
31592     /**
31593      * @cfg {Mixed} value
31594      * The data value of the underlying field (defaults to "")
31595      */
31596     value : "",
31597     /**
31598      * @cfg {String} alignment
31599      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31600      */
31601     alignment: "c-c?",
31602     /**
31603      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31604      * for bottom-right shadow (defaults to "frame")
31605      */
31606     shadow : "frame",
31607     /**
31608      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31609      */
31610     constrain : false,
31611     /**
31612      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31613      */
31614     completeOnEnter : false,
31615     /**
31616      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31617      */
31618     cancelOnEsc : false,
31619     /**
31620      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31621      */
31622     updateEl : false,
31623
31624     // private
31625     onRender : function(ct, position){
31626         this.el = new Roo.Layer({
31627             shadow: this.shadow,
31628             cls: "x-editor",
31629             parentEl : ct,
31630             shim : this.shim,
31631             shadowOffset:4,
31632             id: this.id,
31633             constrain: this.constrain
31634         });
31635         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31636         if(this.field.msgTarget != 'title'){
31637             this.field.msgTarget = 'qtip';
31638         }
31639         this.field.render(this.el);
31640         if(Roo.isGecko){
31641             this.field.el.dom.setAttribute('autocomplete', 'off');
31642         }
31643         this.field.on("specialkey", this.onSpecialKey, this);
31644         if(this.swallowKeys){
31645             this.field.el.swallowEvent(['keydown','keypress']);
31646         }
31647         this.field.show();
31648         this.field.on("blur", this.onBlur, this);
31649         if(this.field.grow){
31650             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31651         }
31652     },
31653
31654     onSpecialKey : function(field, e)
31655     {
31656         //Roo.log('editor onSpecialKey');
31657         if(this.completeOnEnter && e.getKey() == e.ENTER){
31658             e.stopEvent();
31659             this.completeEdit();
31660             return;
31661         }
31662         // do not fire special key otherwise it might hide close the editor...
31663         if(e.getKey() == e.ENTER){    
31664             return;
31665         }
31666         if(this.cancelOnEsc && e.getKey() == e.ESC){
31667             this.cancelEdit();
31668             return;
31669         } 
31670         this.fireEvent('specialkey', field, e);
31671     
31672     },
31673
31674     /**
31675      * Starts the editing process and shows the editor.
31676      * @param {String/HTMLElement/Element} el The element to edit
31677      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31678       * to the innerHTML of el.
31679      */
31680     startEdit : function(el, value){
31681         if(this.editing){
31682             this.completeEdit();
31683         }
31684         this.boundEl = Roo.get(el);
31685         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31686         if(!this.rendered){
31687             this.render(this.parentEl || document.body);
31688         }
31689         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31690             return;
31691         }
31692         this.startValue = v;
31693         this.field.setValue(v);
31694         if(this.autoSize){
31695             var sz = this.boundEl.getSize();
31696             switch(this.autoSize){
31697                 case "width":
31698                 this.setSize(sz.width,  "");
31699                 break;
31700                 case "height":
31701                 this.setSize("",  sz.height);
31702                 break;
31703                 default:
31704                 this.setSize(sz.width,  sz.height);
31705             }
31706         }
31707         this.el.alignTo(this.boundEl, this.alignment);
31708         this.editing = true;
31709         if(Roo.QuickTips){
31710             Roo.QuickTips.disable();
31711         }
31712         this.show();
31713     },
31714
31715     /**
31716      * Sets the height and width of this editor.
31717      * @param {Number} width The new width
31718      * @param {Number} height The new height
31719      */
31720     setSize : function(w, h){
31721         this.field.setSize(w, h);
31722         if(this.el){
31723             this.el.sync();
31724         }
31725     },
31726
31727     /**
31728      * Realigns the editor to the bound field based on the current alignment config value.
31729      */
31730     realign : function(){
31731         this.el.alignTo(this.boundEl, this.alignment);
31732     },
31733
31734     /**
31735      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31736      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31737      */
31738     completeEdit : function(remainVisible){
31739         if(!this.editing){
31740             return;
31741         }
31742         var v = this.getValue();
31743         if(this.revertInvalid !== false && !this.field.isValid()){
31744             v = this.startValue;
31745             this.cancelEdit(true);
31746         }
31747         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31748             this.editing = false;
31749             this.hide();
31750             return;
31751         }
31752         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31753             this.editing = false;
31754             if(this.updateEl && this.boundEl){
31755                 this.boundEl.update(v);
31756             }
31757             if(remainVisible !== true){
31758                 this.hide();
31759             }
31760             this.fireEvent("complete", this, v, this.startValue);
31761         }
31762     },
31763
31764     // private
31765     onShow : function(){
31766         this.el.show();
31767         if(this.hideEl !== false){
31768             this.boundEl.hide();
31769         }
31770         this.field.show();
31771         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31772             this.fixIEFocus = true;
31773             this.deferredFocus.defer(50, this);
31774         }else{
31775             this.field.focus();
31776         }
31777         this.fireEvent("startedit", this.boundEl, this.startValue);
31778     },
31779
31780     deferredFocus : function(){
31781         if(this.editing){
31782             this.field.focus();
31783         }
31784     },
31785
31786     /**
31787      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31788      * reverted to the original starting value.
31789      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31790      * cancel (defaults to false)
31791      */
31792     cancelEdit : function(remainVisible){
31793         if(this.editing){
31794             this.setValue(this.startValue);
31795             if(remainVisible !== true){
31796                 this.hide();
31797             }
31798         }
31799     },
31800
31801     // private
31802     onBlur : function(){
31803         if(this.allowBlur !== true && this.editing){
31804             this.completeEdit();
31805         }
31806     },
31807
31808     // private
31809     onHide : function(){
31810         if(this.editing){
31811             this.completeEdit();
31812             return;
31813         }
31814         this.field.blur();
31815         if(this.field.collapse){
31816             this.field.collapse();
31817         }
31818         this.el.hide();
31819         if(this.hideEl !== false){
31820             this.boundEl.show();
31821         }
31822         if(Roo.QuickTips){
31823             Roo.QuickTips.enable();
31824         }
31825     },
31826
31827     /**
31828      * Sets the data value of the editor
31829      * @param {Mixed} value Any valid value supported by the underlying field
31830      */
31831     setValue : function(v){
31832         this.field.setValue(v);
31833     },
31834
31835     /**
31836      * Gets the data value of the editor
31837      * @return {Mixed} The data value
31838      */
31839     getValue : function(){
31840         return this.field.getValue();
31841     }
31842 });/*
31843  * Based on:
31844  * Ext JS Library 1.1.1
31845  * Copyright(c) 2006-2007, Ext JS, LLC.
31846  *
31847  * Originally Released Under LGPL - original licence link has changed is not relivant.
31848  *
31849  * Fork - LGPL
31850  * <script type="text/javascript">
31851  */
31852  
31853 /**
31854  * @class Roo.BasicDialog
31855  * @extends Roo.util.Observable
31856  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31857  * <pre><code>
31858 var dlg = new Roo.BasicDialog("my-dlg", {
31859     height: 200,
31860     width: 300,
31861     minHeight: 100,
31862     minWidth: 150,
31863     modal: true,
31864     proxyDrag: true,
31865     shadow: true
31866 });
31867 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31868 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31869 dlg.addButton('Cancel', dlg.hide, dlg);
31870 dlg.show();
31871 </code></pre>
31872   <b>A Dialog should always be a direct child of the body element.</b>
31873  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31874  * @cfg {String} title Default text to display in the title bar (defaults to null)
31875  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31876  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31877  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31878  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31879  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31880  * (defaults to null with no animation)
31881  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31882  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31883  * property for valid values (defaults to 'all')
31884  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31885  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31886  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31887  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31888  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31889  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31890  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31891  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31892  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31893  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31894  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31895  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31896  * draggable = true (defaults to false)
31897  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31898  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31899  * shadow (defaults to false)
31900  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31901  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31902  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31903  * @cfg {Array} buttons Array of buttons
31904  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31905  * @constructor
31906  * Create a new BasicDialog.
31907  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31908  * @param {Object} config Configuration options
31909  */
31910 Roo.BasicDialog = function(el, config){
31911     this.el = Roo.get(el);
31912     var dh = Roo.DomHelper;
31913     if(!this.el && config && config.autoCreate){
31914         if(typeof config.autoCreate == "object"){
31915             if(!config.autoCreate.id){
31916                 config.autoCreate.id = el;
31917             }
31918             this.el = dh.append(document.body,
31919                         config.autoCreate, true);
31920         }else{
31921             this.el = dh.append(document.body,
31922                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31923         }
31924     }
31925     el = this.el;
31926     el.setDisplayed(true);
31927     el.hide = this.hideAction;
31928     this.id = el.id;
31929     el.addClass("x-dlg");
31930
31931     Roo.apply(this, config);
31932
31933     this.proxy = el.createProxy("x-dlg-proxy");
31934     this.proxy.hide = this.hideAction;
31935     this.proxy.setOpacity(.5);
31936     this.proxy.hide();
31937
31938     if(config.width){
31939         el.setWidth(config.width);
31940     }
31941     if(config.height){
31942         el.setHeight(config.height);
31943     }
31944     this.size = el.getSize();
31945     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31946         this.xy = [config.x,config.y];
31947     }else{
31948         this.xy = el.getCenterXY(true);
31949     }
31950     /** The header element @type Roo.Element */
31951     this.header = el.child("> .x-dlg-hd");
31952     /** The body element @type Roo.Element */
31953     this.body = el.child("> .x-dlg-bd");
31954     /** The footer element @type Roo.Element */
31955     this.footer = el.child("> .x-dlg-ft");
31956
31957     if(!this.header){
31958         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31959     }
31960     if(!this.body){
31961         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31962     }
31963
31964     this.header.unselectable();
31965     if(this.title){
31966         this.header.update(this.title);
31967     }
31968     // this element allows the dialog to be focused for keyboard event
31969     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31970     this.focusEl.swallowEvent("click", true);
31971
31972     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31973
31974     // wrap the body and footer for special rendering
31975     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31976     if(this.footer){
31977         this.bwrap.dom.appendChild(this.footer.dom);
31978     }
31979
31980     this.bg = this.el.createChild({
31981         tag: "div", cls:"x-dlg-bg",
31982         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31983     });
31984     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31985
31986
31987     if(this.autoScroll !== false && !this.autoTabs){
31988         this.body.setStyle("overflow", "auto");
31989     }
31990
31991     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31992
31993     if(this.closable !== false){
31994         this.el.addClass("x-dlg-closable");
31995         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31996         this.close.on("click", this.closeClick, this);
31997         this.close.addClassOnOver("x-dlg-close-over");
31998     }
31999     if(this.collapsible !== false){
32000         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32001         this.collapseBtn.on("click", this.collapseClick, this);
32002         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32003         this.header.on("dblclick", this.collapseClick, this);
32004     }
32005     if(this.resizable !== false){
32006         this.el.addClass("x-dlg-resizable");
32007         this.resizer = new Roo.Resizable(el, {
32008             minWidth: this.minWidth || 80,
32009             minHeight:this.minHeight || 80,
32010             handles: this.resizeHandles || "all",
32011             pinned: true
32012         });
32013         this.resizer.on("beforeresize", this.beforeResize, this);
32014         this.resizer.on("resize", this.onResize, this);
32015     }
32016     if(this.draggable !== false){
32017         el.addClass("x-dlg-draggable");
32018         if (!this.proxyDrag) {
32019             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32020         }
32021         else {
32022             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32023         }
32024         dd.setHandleElId(this.header.id);
32025         dd.endDrag = this.endMove.createDelegate(this);
32026         dd.startDrag = this.startMove.createDelegate(this);
32027         dd.onDrag = this.onDrag.createDelegate(this);
32028         dd.scroll = false;
32029         this.dd = dd;
32030     }
32031     if(this.modal){
32032         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32033         this.mask.enableDisplayMode("block");
32034         this.mask.hide();
32035         this.el.addClass("x-dlg-modal");
32036     }
32037     if(this.shadow){
32038         this.shadow = new Roo.Shadow({
32039             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32040             offset : this.shadowOffset
32041         });
32042     }else{
32043         this.shadowOffset = 0;
32044     }
32045     if(Roo.useShims && this.shim !== false){
32046         this.shim = this.el.createShim();
32047         this.shim.hide = this.hideAction;
32048         this.shim.hide();
32049     }else{
32050         this.shim = false;
32051     }
32052     if(this.autoTabs){
32053         this.initTabs();
32054     }
32055     if (this.buttons) { 
32056         var bts= this.buttons;
32057         this.buttons = [];
32058         Roo.each(bts, function(b) {
32059             this.addButton(b);
32060         }, this);
32061     }
32062     
32063     
32064     this.addEvents({
32065         /**
32066          * @event keydown
32067          * Fires when a key is pressed
32068          * @param {Roo.BasicDialog} this
32069          * @param {Roo.EventObject} e
32070          */
32071         "keydown" : true,
32072         /**
32073          * @event move
32074          * Fires when this dialog is moved by the user.
32075          * @param {Roo.BasicDialog} this
32076          * @param {Number} x The new page X
32077          * @param {Number} y The new page Y
32078          */
32079         "move" : true,
32080         /**
32081          * @event resize
32082          * Fires when this dialog is resized by the user.
32083          * @param {Roo.BasicDialog} this
32084          * @param {Number} width The new width
32085          * @param {Number} height The new height
32086          */
32087         "resize" : true,
32088         /**
32089          * @event beforehide
32090          * Fires before this dialog is hidden.
32091          * @param {Roo.BasicDialog} this
32092          */
32093         "beforehide" : true,
32094         /**
32095          * @event hide
32096          * Fires when this dialog is hidden.
32097          * @param {Roo.BasicDialog} this
32098          */
32099         "hide" : true,
32100         /**
32101          * @event beforeshow
32102          * Fires before this dialog is shown.
32103          * @param {Roo.BasicDialog} this
32104          */
32105         "beforeshow" : true,
32106         /**
32107          * @event show
32108          * Fires when this dialog is shown.
32109          * @param {Roo.BasicDialog} this
32110          */
32111         "show" : true
32112     });
32113     el.on("keydown", this.onKeyDown, this);
32114     el.on("mousedown", this.toFront, this);
32115     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32116     this.el.hide();
32117     Roo.DialogManager.register(this);
32118     Roo.BasicDialog.superclass.constructor.call(this);
32119 };
32120
32121 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32122     shadowOffset: Roo.isIE ? 6 : 5,
32123     minHeight: 80,
32124     minWidth: 200,
32125     minButtonWidth: 75,
32126     defaultButton: null,
32127     buttonAlign: "right",
32128     tabTag: 'div',
32129     firstShow: true,
32130
32131     /**
32132      * Sets the dialog title text
32133      * @param {String} text The title text to display
32134      * @return {Roo.BasicDialog} this
32135      */
32136     setTitle : function(text){
32137         this.header.update(text);
32138         return this;
32139     },
32140
32141     // private
32142     closeClick : function(){
32143         this.hide();
32144     },
32145
32146     // private
32147     collapseClick : function(){
32148         this[this.collapsed ? "expand" : "collapse"]();
32149     },
32150
32151     /**
32152      * Collapses the dialog to its minimized state (only the title bar is visible).
32153      * Equivalent to the user clicking the collapse dialog button.
32154      */
32155     collapse : function(){
32156         if(!this.collapsed){
32157             this.collapsed = true;
32158             this.el.addClass("x-dlg-collapsed");
32159             this.restoreHeight = this.el.getHeight();
32160             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32161         }
32162     },
32163
32164     /**
32165      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32166      * clicking the expand dialog button.
32167      */
32168     expand : function(){
32169         if(this.collapsed){
32170             this.collapsed = false;
32171             this.el.removeClass("x-dlg-collapsed");
32172             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32173         }
32174     },
32175
32176     /**
32177      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32178      * @return {Roo.TabPanel} The tabs component
32179      */
32180     initTabs : function(){
32181         var tabs = this.getTabs();
32182         while(tabs.getTab(0)){
32183             tabs.removeTab(0);
32184         }
32185         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32186             var dom = el.dom;
32187             tabs.addTab(Roo.id(dom), dom.title);
32188             dom.title = "";
32189         });
32190         tabs.activate(0);
32191         return tabs;
32192     },
32193
32194     // private
32195     beforeResize : function(){
32196         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32197     },
32198
32199     // private
32200     onResize : function(){
32201         this.refreshSize();
32202         this.syncBodyHeight();
32203         this.adjustAssets();
32204         this.focus();
32205         this.fireEvent("resize", this, this.size.width, this.size.height);
32206     },
32207
32208     // private
32209     onKeyDown : function(e){
32210         if(this.isVisible()){
32211             this.fireEvent("keydown", this, e);
32212         }
32213     },
32214
32215     /**
32216      * Resizes the dialog.
32217      * @param {Number} width
32218      * @param {Number} height
32219      * @return {Roo.BasicDialog} this
32220      */
32221     resizeTo : function(width, height){
32222         this.el.setSize(width, height);
32223         this.size = {width: width, height: height};
32224         this.syncBodyHeight();
32225         if(this.fixedcenter){
32226             this.center();
32227         }
32228         if(this.isVisible()){
32229             this.constrainXY();
32230             this.adjustAssets();
32231         }
32232         this.fireEvent("resize", this, width, height);
32233         return this;
32234     },
32235
32236
32237     /**
32238      * Resizes the dialog to fit the specified content size.
32239      * @param {Number} width
32240      * @param {Number} height
32241      * @return {Roo.BasicDialog} this
32242      */
32243     setContentSize : function(w, h){
32244         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32245         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32246         //if(!this.el.isBorderBox()){
32247             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32248             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32249         //}
32250         if(this.tabs){
32251             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32252             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32253         }
32254         this.resizeTo(w, h);
32255         return this;
32256     },
32257
32258     /**
32259      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32260      * executed in response to a particular key being pressed while the dialog is active.
32261      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32262      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32263      * @param {Function} fn The function to call
32264      * @param {Object} scope (optional) The scope of the function
32265      * @return {Roo.BasicDialog} this
32266      */
32267     addKeyListener : function(key, fn, scope){
32268         var keyCode, shift, ctrl, alt;
32269         if(typeof key == "object" && !(key instanceof Array)){
32270             keyCode = key["key"];
32271             shift = key["shift"];
32272             ctrl = key["ctrl"];
32273             alt = key["alt"];
32274         }else{
32275             keyCode = key;
32276         }
32277         var handler = function(dlg, e){
32278             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32279                 var k = e.getKey();
32280                 if(keyCode instanceof Array){
32281                     for(var i = 0, len = keyCode.length; i < len; i++){
32282                         if(keyCode[i] == k){
32283                           fn.call(scope || window, dlg, k, e);
32284                           return;
32285                         }
32286                     }
32287                 }else{
32288                     if(k == keyCode){
32289                         fn.call(scope || window, dlg, k, e);
32290                     }
32291                 }
32292             }
32293         };
32294         this.on("keydown", handler);
32295         return this;
32296     },
32297
32298     /**
32299      * Returns the TabPanel component (creates it if it doesn't exist).
32300      * Note: If you wish to simply check for the existence of tabs without creating them,
32301      * check for a null 'tabs' property.
32302      * @return {Roo.TabPanel} The tabs component
32303      */
32304     getTabs : function(){
32305         if(!this.tabs){
32306             this.el.addClass("x-dlg-auto-tabs");
32307             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32308             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32309         }
32310         return this.tabs;
32311     },
32312
32313     /**
32314      * Adds a button to the footer section of the dialog.
32315      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32316      * object or a valid Roo.DomHelper element config
32317      * @param {Function} handler The function called when the button is clicked
32318      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32319      * @return {Roo.Button} The new button
32320      */
32321     addButton : function(config, handler, scope){
32322         var dh = Roo.DomHelper;
32323         if(!this.footer){
32324             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32325         }
32326         if(!this.btnContainer){
32327             var tb = this.footer.createChild({
32328
32329                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32330                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32331             }, null, true);
32332             this.btnContainer = tb.firstChild.firstChild.firstChild;
32333         }
32334         var bconfig = {
32335             handler: handler,
32336             scope: scope,
32337             minWidth: this.minButtonWidth,
32338             hideParent:true
32339         };
32340         if(typeof config == "string"){
32341             bconfig.text = config;
32342         }else{
32343             if(config.tag){
32344                 bconfig.dhconfig = config;
32345             }else{
32346                 Roo.apply(bconfig, config);
32347             }
32348         }
32349         var fc = false;
32350         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32351             bconfig.position = Math.max(0, bconfig.position);
32352             fc = this.btnContainer.childNodes[bconfig.position];
32353         }
32354          
32355         var btn = new Roo.Button(
32356             fc ? 
32357                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32358                 : this.btnContainer.appendChild(document.createElement("td")),
32359             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32360             bconfig
32361         );
32362         this.syncBodyHeight();
32363         if(!this.buttons){
32364             /**
32365              * Array of all the buttons that have been added to this dialog via addButton
32366              * @type Array
32367              */
32368             this.buttons = [];
32369         }
32370         this.buttons.push(btn);
32371         return btn;
32372     },
32373
32374     /**
32375      * Sets the default button to be focused when the dialog is displayed.
32376      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32377      * @return {Roo.BasicDialog} this
32378      */
32379     setDefaultButton : function(btn){
32380         this.defaultButton = btn;
32381         return this;
32382     },
32383
32384     // private
32385     getHeaderFooterHeight : function(safe){
32386         var height = 0;
32387         if(this.header){
32388            height += this.header.getHeight();
32389         }
32390         if(this.footer){
32391            var fm = this.footer.getMargins();
32392             height += (this.footer.getHeight()+fm.top+fm.bottom);
32393         }
32394         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32395         height += this.centerBg.getPadding("tb");
32396         return height;
32397     },
32398
32399     // private
32400     syncBodyHeight : function()
32401     {
32402         var bd = this.body, // the text
32403             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32404             bw = this.bwrap;
32405         var height = this.size.height - this.getHeaderFooterHeight(false);
32406         bd.setHeight(height-bd.getMargins("tb"));
32407         var hh = this.header.getHeight();
32408         var h = this.size.height-hh;
32409         cb.setHeight(h);
32410         
32411         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32412         bw.setHeight(h-cb.getPadding("tb"));
32413         
32414         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32415         bd.setWidth(bw.getWidth(true));
32416         if(this.tabs){
32417             this.tabs.syncHeight();
32418             if(Roo.isIE){
32419                 this.tabs.el.repaint();
32420             }
32421         }
32422     },
32423
32424     /**
32425      * Restores the previous state of the dialog if Roo.state is configured.
32426      * @return {Roo.BasicDialog} this
32427      */
32428     restoreState : function(){
32429         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32430         if(box && box.width){
32431             this.xy = [box.x, box.y];
32432             this.resizeTo(box.width, box.height);
32433         }
32434         return this;
32435     },
32436
32437     // private
32438     beforeShow : function(){
32439         this.expand();
32440         if(this.fixedcenter){
32441             this.xy = this.el.getCenterXY(true);
32442         }
32443         if(this.modal){
32444             Roo.get(document.body).addClass("x-body-masked");
32445             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32446             this.mask.show();
32447         }
32448         this.constrainXY();
32449     },
32450
32451     // private
32452     animShow : function(){
32453         var b = Roo.get(this.animateTarget).getBox();
32454         this.proxy.setSize(b.width, b.height);
32455         this.proxy.setLocation(b.x, b.y);
32456         this.proxy.show();
32457         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32458                     true, .35, this.showEl.createDelegate(this));
32459     },
32460
32461     /**
32462      * Shows the dialog.
32463      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32464      * @return {Roo.BasicDialog} this
32465      */
32466     show : function(animateTarget){
32467         if (this.fireEvent("beforeshow", this) === false){
32468             return;
32469         }
32470         if(this.syncHeightBeforeShow){
32471             this.syncBodyHeight();
32472         }else if(this.firstShow){
32473             this.firstShow = false;
32474             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32475         }
32476         this.animateTarget = animateTarget || this.animateTarget;
32477         if(!this.el.isVisible()){
32478             this.beforeShow();
32479             if(this.animateTarget && Roo.get(this.animateTarget)){
32480                 this.animShow();
32481             }else{
32482                 this.showEl();
32483             }
32484         }
32485         return this;
32486     },
32487
32488     // private
32489     showEl : function(){
32490         this.proxy.hide();
32491         this.el.setXY(this.xy);
32492         this.el.show();
32493         this.adjustAssets(true);
32494         this.toFront();
32495         this.focus();
32496         // IE peekaboo bug - fix found by Dave Fenwick
32497         if(Roo.isIE){
32498             this.el.repaint();
32499         }
32500         this.fireEvent("show", this);
32501     },
32502
32503     /**
32504      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32505      * dialog itself will receive focus.
32506      */
32507     focus : function(){
32508         if(this.defaultButton){
32509             this.defaultButton.focus();
32510         }else{
32511             this.focusEl.focus();
32512         }
32513     },
32514
32515     // private
32516     constrainXY : function(){
32517         if(this.constraintoviewport !== false){
32518             if(!this.viewSize){
32519                 if(this.container){
32520                     var s = this.container.getSize();
32521                     this.viewSize = [s.width, s.height];
32522                 }else{
32523                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32524                 }
32525             }
32526             var s = Roo.get(this.container||document).getScroll();
32527
32528             var x = this.xy[0], y = this.xy[1];
32529             var w = this.size.width, h = this.size.height;
32530             var vw = this.viewSize[0], vh = this.viewSize[1];
32531             // only move it if it needs it
32532             var moved = false;
32533             // first validate right/bottom
32534             if(x + w > vw+s.left){
32535                 x = vw - w;
32536                 moved = true;
32537             }
32538             if(y + h > vh+s.top){
32539                 y = vh - h;
32540                 moved = true;
32541             }
32542             // then make sure top/left isn't negative
32543             if(x < s.left){
32544                 x = s.left;
32545                 moved = true;
32546             }
32547             if(y < s.top){
32548                 y = s.top;
32549                 moved = true;
32550             }
32551             if(moved){
32552                 // cache xy
32553                 this.xy = [x, y];
32554                 if(this.isVisible()){
32555                     this.el.setLocation(x, y);
32556                     this.adjustAssets();
32557                 }
32558             }
32559         }
32560     },
32561
32562     // private
32563     onDrag : function(){
32564         if(!this.proxyDrag){
32565             this.xy = this.el.getXY();
32566             this.adjustAssets();
32567         }
32568     },
32569
32570     // private
32571     adjustAssets : function(doShow){
32572         var x = this.xy[0], y = this.xy[1];
32573         var w = this.size.width, h = this.size.height;
32574         if(doShow === true){
32575             if(this.shadow){
32576                 this.shadow.show(this.el);
32577             }
32578             if(this.shim){
32579                 this.shim.show();
32580             }
32581         }
32582         if(this.shadow && this.shadow.isVisible()){
32583             this.shadow.show(this.el);
32584         }
32585         if(this.shim && this.shim.isVisible()){
32586             this.shim.setBounds(x, y, w, h);
32587         }
32588     },
32589
32590     // private
32591     adjustViewport : function(w, h){
32592         if(!w || !h){
32593             w = Roo.lib.Dom.getViewWidth();
32594             h = Roo.lib.Dom.getViewHeight();
32595         }
32596         // cache the size
32597         this.viewSize = [w, h];
32598         if(this.modal && this.mask.isVisible()){
32599             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32600             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32601         }
32602         if(this.isVisible()){
32603             this.constrainXY();
32604         }
32605     },
32606
32607     /**
32608      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32609      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32610      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32611      */
32612     destroy : function(removeEl){
32613         if(this.isVisible()){
32614             this.animateTarget = null;
32615             this.hide();
32616         }
32617         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32618         if(this.tabs){
32619             this.tabs.destroy(removeEl);
32620         }
32621         Roo.destroy(
32622              this.shim,
32623              this.proxy,
32624              this.resizer,
32625              this.close,
32626              this.mask
32627         );
32628         if(this.dd){
32629             this.dd.unreg();
32630         }
32631         if(this.buttons){
32632            for(var i = 0, len = this.buttons.length; i < len; i++){
32633                this.buttons[i].destroy();
32634            }
32635         }
32636         this.el.removeAllListeners();
32637         if(removeEl === true){
32638             this.el.update("");
32639             this.el.remove();
32640         }
32641         Roo.DialogManager.unregister(this);
32642     },
32643
32644     // private
32645     startMove : function(){
32646         if(this.proxyDrag){
32647             this.proxy.show();
32648         }
32649         if(this.constraintoviewport !== false){
32650             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32651         }
32652     },
32653
32654     // private
32655     endMove : function(){
32656         if(!this.proxyDrag){
32657             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32658         }else{
32659             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32660             this.proxy.hide();
32661         }
32662         this.refreshSize();
32663         this.adjustAssets();
32664         this.focus();
32665         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32666     },
32667
32668     /**
32669      * Brings this dialog to the front of any other visible dialogs
32670      * @return {Roo.BasicDialog} this
32671      */
32672     toFront : function(){
32673         Roo.DialogManager.bringToFront(this);
32674         return this;
32675     },
32676
32677     /**
32678      * Sends this dialog to the back (under) of any other visible dialogs
32679      * @return {Roo.BasicDialog} this
32680      */
32681     toBack : function(){
32682         Roo.DialogManager.sendToBack(this);
32683         return this;
32684     },
32685
32686     /**
32687      * Centers this dialog in the viewport
32688      * @return {Roo.BasicDialog} this
32689      */
32690     center : function(){
32691         var xy = this.el.getCenterXY(true);
32692         this.moveTo(xy[0], xy[1]);
32693         return this;
32694     },
32695
32696     /**
32697      * Moves the dialog's top-left corner to the specified point
32698      * @param {Number} x
32699      * @param {Number} y
32700      * @return {Roo.BasicDialog} this
32701      */
32702     moveTo : function(x, y){
32703         this.xy = [x,y];
32704         if(this.isVisible()){
32705             this.el.setXY(this.xy);
32706             this.adjustAssets();
32707         }
32708         return this;
32709     },
32710
32711     /**
32712      * Aligns the dialog to the specified element
32713      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32714      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32715      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32716      * @return {Roo.BasicDialog} this
32717      */
32718     alignTo : function(element, position, offsets){
32719         this.xy = this.el.getAlignToXY(element, position, offsets);
32720         if(this.isVisible()){
32721             this.el.setXY(this.xy);
32722             this.adjustAssets();
32723         }
32724         return this;
32725     },
32726
32727     /**
32728      * Anchors an element to another element and realigns it when the window is resized.
32729      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32730      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32731      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32732      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32733      * is a number, it is used as the buffer delay (defaults to 50ms).
32734      * @return {Roo.BasicDialog} this
32735      */
32736     anchorTo : function(el, alignment, offsets, monitorScroll){
32737         var action = function(){
32738             this.alignTo(el, alignment, offsets);
32739         };
32740         Roo.EventManager.onWindowResize(action, this);
32741         var tm = typeof monitorScroll;
32742         if(tm != 'undefined'){
32743             Roo.EventManager.on(window, 'scroll', action, this,
32744                 {buffer: tm == 'number' ? monitorScroll : 50});
32745         }
32746         action.call(this);
32747         return this;
32748     },
32749
32750     /**
32751      * Returns true if the dialog is visible
32752      * @return {Boolean}
32753      */
32754     isVisible : function(){
32755         return this.el.isVisible();
32756     },
32757
32758     // private
32759     animHide : function(callback){
32760         var b = Roo.get(this.animateTarget).getBox();
32761         this.proxy.show();
32762         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32763         this.el.hide();
32764         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32765                     this.hideEl.createDelegate(this, [callback]));
32766     },
32767
32768     /**
32769      * Hides the dialog.
32770      * @param {Function} callback (optional) Function to call when the dialog is hidden
32771      * @return {Roo.BasicDialog} this
32772      */
32773     hide : function(callback){
32774         if (this.fireEvent("beforehide", this) === false){
32775             return;
32776         }
32777         if(this.shadow){
32778             this.shadow.hide();
32779         }
32780         if(this.shim) {
32781           this.shim.hide();
32782         }
32783         // sometimes animateTarget seems to get set.. causing problems...
32784         // this just double checks..
32785         if(this.animateTarget && Roo.get(this.animateTarget)) {
32786            this.animHide(callback);
32787         }else{
32788             this.el.hide();
32789             this.hideEl(callback);
32790         }
32791         return this;
32792     },
32793
32794     // private
32795     hideEl : function(callback){
32796         this.proxy.hide();
32797         if(this.modal){
32798             this.mask.hide();
32799             Roo.get(document.body).removeClass("x-body-masked");
32800         }
32801         this.fireEvent("hide", this);
32802         if(typeof callback == "function"){
32803             callback();
32804         }
32805     },
32806
32807     // private
32808     hideAction : function(){
32809         this.setLeft("-10000px");
32810         this.setTop("-10000px");
32811         this.setStyle("visibility", "hidden");
32812     },
32813
32814     // private
32815     refreshSize : function(){
32816         this.size = this.el.getSize();
32817         this.xy = this.el.getXY();
32818         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32819     },
32820
32821     // private
32822     // z-index is managed by the DialogManager and may be overwritten at any time
32823     setZIndex : function(index){
32824         if(this.modal){
32825             this.mask.setStyle("z-index", index);
32826         }
32827         if(this.shim){
32828             this.shim.setStyle("z-index", ++index);
32829         }
32830         if(this.shadow){
32831             this.shadow.setZIndex(++index);
32832         }
32833         this.el.setStyle("z-index", ++index);
32834         if(this.proxy){
32835             this.proxy.setStyle("z-index", ++index);
32836         }
32837         if(this.resizer){
32838             this.resizer.proxy.setStyle("z-index", ++index);
32839         }
32840
32841         this.lastZIndex = index;
32842     },
32843
32844     /**
32845      * Returns the element for this dialog
32846      * @return {Roo.Element} The underlying dialog Element
32847      */
32848     getEl : function(){
32849         return this.el;
32850     }
32851 });
32852
32853 /**
32854  * @class Roo.DialogManager
32855  * Provides global access to BasicDialogs that have been created and
32856  * support for z-indexing (layering) multiple open dialogs.
32857  */
32858 Roo.DialogManager = function(){
32859     var list = {};
32860     var accessList = [];
32861     var front = null;
32862
32863     // private
32864     var sortDialogs = function(d1, d2){
32865         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32866     };
32867
32868     // private
32869     var orderDialogs = function(){
32870         accessList.sort(sortDialogs);
32871         var seed = Roo.DialogManager.zseed;
32872         for(var i = 0, len = accessList.length; i < len; i++){
32873             var dlg = accessList[i];
32874             if(dlg){
32875                 dlg.setZIndex(seed + (i*10));
32876             }
32877         }
32878     };
32879
32880     return {
32881         /**
32882          * The starting z-index for BasicDialogs (defaults to 9000)
32883          * @type Number The z-index value
32884          */
32885         zseed : 9000,
32886
32887         // private
32888         register : function(dlg){
32889             list[dlg.id] = dlg;
32890             accessList.push(dlg);
32891         },
32892
32893         // private
32894         unregister : function(dlg){
32895             delete list[dlg.id];
32896             var i=0;
32897             var len=0;
32898             if(!accessList.indexOf){
32899                 for(  i = 0, len = accessList.length; i < len; i++){
32900                     if(accessList[i] == dlg){
32901                         accessList.splice(i, 1);
32902                         return;
32903                     }
32904                 }
32905             }else{
32906                  i = accessList.indexOf(dlg);
32907                 if(i != -1){
32908                     accessList.splice(i, 1);
32909                 }
32910             }
32911         },
32912
32913         /**
32914          * Gets a registered dialog by id
32915          * @param {String/Object} id The id of the dialog or a dialog
32916          * @return {Roo.BasicDialog} this
32917          */
32918         get : function(id){
32919             return typeof id == "object" ? id : list[id];
32920         },
32921
32922         /**
32923          * Brings the specified dialog to the front
32924          * @param {String/Object} dlg The id of the dialog or a dialog
32925          * @return {Roo.BasicDialog} this
32926          */
32927         bringToFront : function(dlg){
32928             dlg = this.get(dlg);
32929             if(dlg != front){
32930                 front = dlg;
32931                 dlg._lastAccess = new Date().getTime();
32932                 orderDialogs();
32933             }
32934             return dlg;
32935         },
32936
32937         /**
32938          * Sends the specified dialog to the back
32939          * @param {String/Object} dlg The id of the dialog or a dialog
32940          * @return {Roo.BasicDialog} this
32941          */
32942         sendToBack : function(dlg){
32943             dlg = this.get(dlg);
32944             dlg._lastAccess = -(new Date().getTime());
32945             orderDialogs();
32946             return dlg;
32947         },
32948
32949         /**
32950          * Hides all dialogs
32951          */
32952         hideAll : function(){
32953             for(var id in list){
32954                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32955                     list[id].hide();
32956                 }
32957             }
32958         }
32959     };
32960 }();
32961
32962 /**
32963  * @class Roo.LayoutDialog
32964  * @extends Roo.BasicDialog
32965  * Dialog which provides adjustments for working with a layout in a Dialog.
32966  * Add your necessary layout config options to the dialog's config.<br>
32967  * Example usage (including a nested layout):
32968  * <pre><code>
32969 if(!dialog){
32970     dialog = new Roo.LayoutDialog("download-dlg", {
32971         modal: true,
32972         width:600,
32973         height:450,
32974         shadow:true,
32975         minWidth:500,
32976         minHeight:350,
32977         autoTabs:true,
32978         proxyDrag:true,
32979         // layout config merges with the dialog config
32980         center:{
32981             tabPosition: "top",
32982             alwaysShowTabs: true
32983         }
32984     });
32985     dialog.addKeyListener(27, dialog.hide, dialog);
32986     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32987     dialog.addButton("Build It!", this.getDownload, this);
32988
32989     // we can even add nested layouts
32990     var innerLayout = new Roo.BorderLayout("dl-inner", {
32991         east: {
32992             initialSize: 200,
32993             autoScroll:true,
32994             split:true
32995         },
32996         center: {
32997             autoScroll:true
32998         }
32999     });
33000     innerLayout.beginUpdate();
33001     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33002     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33003     innerLayout.endUpdate(true);
33004
33005     var layout = dialog.getLayout();
33006     layout.beginUpdate();
33007     layout.add("center", new Roo.ContentPanel("standard-panel",
33008                         {title: "Download the Source", fitToFrame:true}));
33009     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33010                {title: "Build your own roo.js"}));
33011     layout.getRegion("center").showPanel(sp);
33012     layout.endUpdate();
33013 }
33014 </code></pre>
33015     * @constructor
33016     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33017     * @param {Object} config configuration options
33018   */
33019 Roo.LayoutDialog = function(el, cfg){
33020     
33021     var config=  cfg;
33022     if (typeof(cfg) == 'undefined') {
33023         config = Roo.apply({}, el);
33024         // not sure why we use documentElement here.. - it should always be body.
33025         // IE7 borks horribly if we use documentElement.
33026         // webkit also does not like documentElement - it creates a body element...
33027         el = Roo.get( document.body || document.documentElement ).createChild();
33028         //config.autoCreate = true;
33029     }
33030     
33031     
33032     config.autoTabs = false;
33033     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33034     this.body.setStyle({overflow:"hidden", position:"relative"});
33035     this.layout = new Roo.BorderLayout(this.body.dom, config);
33036     this.layout.monitorWindowResize = false;
33037     this.el.addClass("x-dlg-auto-layout");
33038     // fix case when center region overwrites center function
33039     this.center = Roo.BasicDialog.prototype.center;
33040     this.on("show", this.layout.layout, this.layout, true);
33041     if (config.items) {
33042         var xitems = config.items;
33043         delete config.items;
33044         Roo.each(xitems, this.addxtype, this);
33045     }
33046     
33047     
33048 };
33049 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33050     /**
33051      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33052      * @deprecated
33053      */
33054     endUpdate : function(){
33055         this.layout.endUpdate();
33056     },
33057
33058     /**
33059      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33060      *  @deprecated
33061      */
33062     beginUpdate : function(){
33063         this.layout.beginUpdate();
33064     },
33065
33066     /**
33067      * Get the BorderLayout for this dialog
33068      * @return {Roo.BorderLayout}
33069      */
33070     getLayout : function(){
33071         return this.layout;
33072     },
33073
33074     showEl : function(){
33075         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33076         if(Roo.isIE7){
33077             this.layout.layout();
33078         }
33079     },
33080
33081     // private
33082     // Use the syncHeightBeforeShow config option to control this automatically
33083     syncBodyHeight : function(){
33084         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33085         if(this.layout){this.layout.layout();}
33086     },
33087     
33088       /**
33089      * Add an xtype element (actually adds to the layout.)
33090      * @return {Object} xdata xtype object data.
33091      */
33092     
33093     addxtype : function(c) {
33094         return this.layout.addxtype(c);
33095     }
33096 });/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106  
33107 /**
33108  * @class Roo.MessageBox
33109  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33110  * Example usage:
33111  *<pre><code>
33112 // Basic alert:
33113 Roo.Msg.alert('Status', 'Changes saved successfully.');
33114
33115 // Prompt for user data:
33116 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33117     if (btn == 'ok'){
33118         // process text value...
33119     }
33120 });
33121
33122 // Show a dialog using config options:
33123 Roo.Msg.show({
33124    title:'Save Changes?',
33125    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33126    buttons: Roo.Msg.YESNOCANCEL,
33127    fn: processResult,
33128    animEl: 'elId'
33129 });
33130 </code></pre>
33131  * @singleton
33132  */
33133 Roo.MessageBox = function(){
33134     var dlg, opt, mask, waitTimer;
33135     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33136     var buttons, activeTextEl, bwidth;
33137
33138     // private
33139     var handleButton = function(button){
33140         dlg.hide();
33141         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33142     };
33143
33144     // private
33145     var handleHide = function(){
33146         if(opt && opt.cls){
33147             dlg.el.removeClass(opt.cls);
33148         }
33149         if(waitTimer){
33150             Roo.TaskMgr.stop(waitTimer);
33151             waitTimer = null;
33152         }
33153     };
33154
33155     // private
33156     var updateButtons = function(b){
33157         var width = 0;
33158         if(!b){
33159             buttons["ok"].hide();
33160             buttons["cancel"].hide();
33161             buttons["yes"].hide();
33162             buttons["no"].hide();
33163             dlg.footer.dom.style.display = 'none';
33164             return width;
33165         }
33166         dlg.footer.dom.style.display = '';
33167         for(var k in buttons){
33168             if(typeof buttons[k] != "function"){
33169                 if(b[k]){
33170                     buttons[k].show();
33171                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33172                     width += buttons[k].el.getWidth()+15;
33173                 }else{
33174                     buttons[k].hide();
33175                 }
33176             }
33177         }
33178         return width;
33179     };
33180
33181     // private
33182     var handleEsc = function(d, k, e){
33183         if(opt && opt.closable !== false){
33184             dlg.hide();
33185         }
33186         if(e){
33187             e.stopEvent();
33188         }
33189     };
33190
33191     return {
33192         /**
33193          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33194          * @return {Roo.BasicDialog} The BasicDialog element
33195          */
33196         getDialog : function(){
33197            if(!dlg){
33198                 dlg = new Roo.BasicDialog("x-msg-box", {
33199                     autoCreate : true,
33200                     shadow: true,
33201                     draggable: true,
33202                     resizable:false,
33203                     constraintoviewport:false,
33204                     fixedcenter:true,
33205                     collapsible : false,
33206                     shim:true,
33207                     modal: true,
33208                     width:400, height:100,
33209                     buttonAlign:"center",
33210                     closeClick : function(){
33211                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33212                             handleButton("no");
33213                         }else{
33214                             handleButton("cancel");
33215                         }
33216                     }
33217                 });
33218                 dlg.on("hide", handleHide);
33219                 mask = dlg.mask;
33220                 dlg.addKeyListener(27, handleEsc);
33221                 buttons = {};
33222                 var bt = this.buttonText;
33223                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33224                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33225                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33226                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33227                 bodyEl = dlg.body.createChild({
33228
33229                     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>'
33230                 });
33231                 msgEl = bodyEl.dom.firstChild;
33232                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33233                 textboxEl.enableDisplayMode();
33234                 textboxEl.addKeyListener([10,13], function(){
33235                     if(dlg.isVisible() && opt && opt.buttons){
33236                         if(opt.buttons.ok){
33237                             handleButton("ok");
33238                         }else if(opt.buttons.yes){
33239                             handleButton("yes");
33240                         }
33241                     }
33242                 });
33243                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33244                 textareaEl.enableDisplayMode();
33245                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33246                 progressEl.enableDisplayMode();
33247                 var pf = progressEl.dom.firstChild;
33248                 if (pf) {
33249                     pp = Roo.get(pf.firstChild);
33250                     pp.setHeight(pf.offsetHeight);
33251                 }
33252                 
33253             }
33254             return dlg;
33255         },
33256
33257         /**
33258          * Updates the message box body text
33259          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33260          * the XHTML-compliant non-breaking space character '&amp;#160;')
33261          * @return {Roo.MessageBox} This message box
33262          */
33263         updateText : function(text){
33264             if(!dlg.isVisible() && !opt.width){
33265                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33266             }
33267             msgEl.innerHTML = text || '&#160;';
33268       
33269             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33270             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33271             var w = Math.max(
33272                     Math.min(opt.width || cw , this.maxWidth), 
33273                     Math.max(opt.minWidth || this.minWidth, bwidth)
33274             );
33275             if(opt.prompt){
33276                 activeTextEl.setWidth(w);
33277             }
33278             if(dlg.isVisible()){
33279                 dlg.fixedcenter = false;
33280             }
33281             // to big, make it scroll. = But as usual stupid IE does not support
33282             // !important..
33283             
33284             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33285                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33286                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33287             } else {
33288                 bodyEl.dom.style.height = '';
33289                 bodyEl.dom.style.overflowY = '';
33290             }
33291             if (cw > w) {
33292                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33293             } else {
33294                 bodyEl.dom.style.overflowX = '';
33295             }
33296             
33297             dlg.setContentSize(w, bodyEl.getHeight());
33298             if(dlg.isVisible()){
33299                 dlg.fixedcenter = true;
33300             }
33301             return this;
33302         },
33303
33304         /**
33305          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33306          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33307          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33308          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33309          * @return {Roo.MessageBox} This message box
33310          */
33311         updateProgress : function(value, text){
33312             if(text){
33313                 this.updateText(text);
33314             }
33315             if (pp) { // weird bug on my firefox - for some reason this is not defined
33316                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33317             }
33318             return this;
33319         },        
33320
33321         /**
33322          * Returns true if the message box is currently displayed
33323          * @return {Boolean} True if the message box is visible, else false
33324          */
33325         isVisible : function(){
33326             return dlg && dlg.isVisible();  
33327         },
33328
33329         /**
33330          * Hides the message box if it is displayed
33331          */
33332         hide : function(){
33333             if(this.isVisible()){
33334                 dlg.hide();
33335             }  
33336         },
33337
33338         /**
33339          * Displays a new message box, or reinitializes an existing message box, based on the config options
33340          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33341          * The following config object properties are supported:
33342          * <pre>
33343 Property    Type             Description
33344 ----------  ---------------  ------------------------------------------------------------------------------------
33345 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33346                                    closes (defaults to undefined)
33347 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33348                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33349 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33350                                    progress and wait dialogs will ignore this property and always hide the
33351                                    close button as they can only be closed programmatically.
33352 cls               String           A custom CSS class to apply to the message box element
33353 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33354                                    displayed (defaults to 75)
33355 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33356                                    function will be btn (the name of the button that was clicked, if applicable,
33357                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33358                                    Progress and wait dialogs will ignore this option since they do not respond to
33359                                    user actions and can only be closed programmatically, so any required function
33360                                    should be called by the same code after it closes the dialog.
33361 icon              String           A CSS class that provides a background image to be used as an icon for
33362                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33363 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33364 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33365 modal             Boolean          False to allow user interaction with the page while the message box is
33366                                    displayed (defaults to true)
33367 msg               String           A string that will replace the existing message box body text (defaults
33368                                    to the XHTML-compliant non-breaking space character '&#160;')
33369 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33370 progress          Boolean          True to display a progress bar (defaults to false)
33371 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33372 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33373 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33374 title             String           The title text
33375 value             String           The string value to set into the active textbox element if displayed
33376 wait              Boolean          True to display a progress bar (defaults to false)
33377 width             Number           The width of the dialog in pixels
33378 </pre>
33379          *
33380          * Example usage:
33381          * <pre><code>
33382 Roo.Msg.show({
33383    title: 'Address',
33384    msg: 'Please enter your address:',
33385    width: 300,
33386    buttons: Roo.MessageBox.OKCANCEL,
33387    multiline: true,
33388    fn: saveAddress,
33389    animEl: 'addAddressBtn'
33390 });
33391 </code></pre>
33392          * @param {Object} config Configuration options
33393          * @return {Roo.MessageBox} This message box
33394          */
33395         show : function(options)
33396         {
33397             
33398             // this causes nightmares if you show one dialog after another
33399             // especially on callbacks..
33400              
33401             if(this.isVisible()){
33402                 
33403                 this.hide();
33404                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33405                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33406                 Roo.log("New Dialog Message:" +  options.msg )
33407                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33408                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33409                 
33410             }
33411             var d = this.getDialog();
33412             opt = options;
33413             d.setTitle(opt.title || "&#160;");
33414             d.close.setDisplayed(opt.closable !== false);
33415             activeTextEl = textboxEl;
33416             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33417             if(opt.prompt){
33418                 if(opt.multiline){
33419                     textboxEl.hide();
33420                     textareaEl.show();
33421                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33422                         opt.multiline : this.defaultTextHeight);
33423                     activeTextEl = textareaEl;
33424                 }else{
33425                     textboxEl.show();
33426                     textareaEl.hide();
33427                 }
33428             }else{
33429                 textboxEl.hide();
33430                 textareaEl.hide();
33431             }
33432             progressEl.setDisplayed(opt.progress === true);
33433             this.updateProgress(0);
33434             activeTextEl.dom.value = opt.value || "";
33435             if(opt.prompt){
33436                 dlg.setDefaultButton(activeTextEl);
33437             }else{
33438                 var bs = opt.buttons;
33439                 var db = null;
33440                 if(bs && bs.ok){
33441                     db = buttons["ok"];
33442                 }else if(bs && bs.yes){
33443                     db = buttons["yes"];
33444                 }
33445                 dlg.setDefaultButton(db);
33446             }
33447             bwidth = updateButtons(opt.buttons);
33448             this.updateText(opt.msg);
33449             if(opt.cls){
33450                 d.el.addClass(opt.cls);
33451             }
33452             d.proxyDrag = opt.proxyDrag === true;
33453             d.modal = opt.modal !== false;
33454             d.mask = opt.modal !== false ? mask : false;
33455             if(!d.isVisible()){
33456                 // force it to the end of the z-index stack so it gets a cursor in FF
33457                 document.body.appendChild(dlg.el.dom);
33458                 d.animateTarget = null;
33459                 d.show(options.animEl);
33460             }
33461             return this;
33462         },
33463
33464         /**
33465          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33466          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33467          * and closing the message box when the process is complete.
33468          * @param {String} title The title bar text
33469          * @param {String} msg The message box body text
33470          * @return {Roo.MessageBox} This message box
33471          */
33472         progress : function(title, msg){
33473             this.show({
33474                 title : title,
33475                 msg : msg,
33476                 buttons: false,
33477                 progress:true,
33478                 closable:false,
33479                 minWidth: this.minProgressWidth,
33480                 modal : true
33481             });
33482             return this;
33483         },
33484
33485         /**
33486          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33487          * If a callback function is passed it will be called after the user clicks the button, and the
33488          * id of the button that was clicked will be passed as the only parameter to the callback
33489          * (could also be the top-right close button).
33490          * @param {String} title The title bar text
33491          * @param {String} msg The message box body text
33492          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33493          * @param {Object} scope (optional) The scope of the callback function
33494          * @return {Roo.MessageBox} This message box
33495          */
33496         alert : function(title, msg, fn, scope){
33497             this.show({
33498                 title : title,
33499                 msg : msg,
33500                 buttons: this.OK,
33501                 fn: fn,
33502                 scope : scope,
33503                 modal : true
33504             });
33505             return this;
33506         },
33507
33508         /**
33509          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33510          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33511          * You are responsible for closing the message box when the process is complete.
33512          * @param {String} msg The message box body text
33513          * @param {String} title (optional) The title bar text
33514          * @return {Roo.MessageBox} This message box
33515          */
33516         wait : function(msg, title){
33517             this.show({
33518                 title : title,
33519                 msg : msg,
33520                 buttons: false,
33521                 closable:false,
33522                 progress:true,
33523                 modal:true,
33524                 width:300,
33525                 wait:true
33526             });
33527             waitTimer = Roo.TaskMgr.start({
33528                 run: function(i){
33529                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33530                 },
33531                 interval: 1000
33532             });
33533             return this;
33534         },
33535
33536         /**
33537          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33538          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33539          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33540          * @param {String} title The title bar text
33541          * @param {String} msg The message box body text
33542          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33543          * @param {Object} scope (optional) The scope of the callback function
33544          * @return {Roo.MessageBox} This message box
33545          */
33546         confirm : function(title, msg, fn, scope){
33547             this.show({
33548                 title : title,
33549                 msg : msg,
33550                 buttons: this.YESNO,
33551                 fn: fn,
33552                 scope : scope,
33553                 modal : true
33554             });
33555             return this;
33556         },
33557
33558         /**
33559          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33560          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33561          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33562          * (could also be the top-right close button) and the text that was entered will be passed as the two
33563          * parameters to the callback.
33564          * @param {String} title The title bar text
33565          * @param {String} msg The message box body text
33566          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33567          * @param {Object} scope (optional) The scope of the callback function
33568          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33569          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33570          * @return {Roo.MessageBox} This message box
33571          */
33572         prompt : function(title, msg, fn, scope, multiline){
33573             this.show({
33574                 title : title,
33575                 msg : msg,
33576                 buttons: this.OKCANCEL,
33577                 fn: fn,
33578                 minWidth:250,
33579                 scope : scope,
33580                 prompt:true,
33581                 multiline: multiline,
33582                 modal : true
33583             });
33584             return this;
33585         },
33586
33587         /**
33588          * Button config that displays a single OK button
33589          * @type Object
33590          */
33591         OK : {ok:true},
33592         /**
33593          * Button config that displays Yes and No buttons
33594          * @type Object
33595          */
33596         YESNO : {yes:true, no:true},
33597         /**
33598          * Button config that displays OK and Cancel buttons
33599          * @type Object
33600          */
33601         OKCANCEL : {ok:true, cancel:true},
33602         /**
33603          * Button config that displays Yes, No and Cancel buttons
33604          * @type Object
33605          */
33606         YESNOCANCEL : {yes:true, no:true, cancel:true},
33607
33608         /**
33609          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33610          * @type Number
33611          */
33612         defaultTextHeight : 75,
33613         /**
33614          * The maximum width in pixels of the message box (defaults to 600)
33615          * @type Number
33616          */
33617         maxWidth : 600,
33618         /**
33619          * The minimum width in pixels of the message box (defaults to 100)
33620          * @type Number
33621          */
33622         minWidth : 100,
33623         /**
33624          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33625          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33626          * @type Number
33627          */
33628         minProgressWidth : 250,
33629         /**
33630          * An object containing the default button text strings that can be overriden for localized language support.
33631          * Supported properties are: ok, cancel, yes and no.
33632          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33633          * @type Object
33634          */
33635         buttonText : {
33636             ok : "OK",
33637             cancel : "Cancel",
33638             yes : "Yes",
33639             no : "No"
33640         }
33641     };
33642 }();
33643
33644 /**
33645  * Shorthand for {@link Roo.MessageBox}
33646  */
33647 Roo.Msg = Roo.MessageBox;/*
33648  * Based on:
33649  * Ext JS Library 1.1.1
33650  * Copyright(c) 2006-2007, Ext JS, LLC.
33651  *
33652  * Originally Released Under LGPL - original licence link has changed is not relivant.
33653  *
33654  * Fork - LGPL
33655  * <script type="text/javascript">
33656  */
33657 /**
33658  * @class Roo.QuickTips
33659  * Provides attractive and customizable tooltips for any element.
33660  * @singleton
33661  */
33662 Roo.QuickTips = function(){
33663     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33664     var ce, bd, xy, dd;
33665     var visible = false, disabled = true, inited = false;
33666     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33667     
33668     var onOver = function(e){
33669         if(disabled){
33670             return;
33671         }
33672         var t = e.getTarget();
33673         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33674             return;
33675         }
33676         if(ce && t == ce.el){
33677             clearTimeout(hideProc);
33678             return;
33679         }
33680         if(t && tagEls[t.id]){
33681             tagEls[t.id].el = t;
33682             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33683             return;
33684         }
33685         var ttp, et = Roo.fly(t);
33686         var ns = cfg.namespace;
33687         if(tm.interceptTitles && t.title){
33688             ttp = t.title;
33689             t.qtip = ttp;
33690             t.removeAttribute("title");
33691             e.preventDefault();
33692         }else{
33693             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33694         }
33695         if(ttp){
33696             showProc = show.defer(tm.showDelay, tm, [{
33697                 el: t, 
33698                 text: ttp.replace(/\\n/g,'<br/>'),
33699                 width: et.getAttributeNS(ns, cfg.width),
33700                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33701                 title: et.getAttributeNS(ns, cfg.title),
33702                     cls: et.getAttributeNS(ns, cfg.cls)
33703             }]);
33704         }
33705     };
33706     
33707     var onOut = function(e){
33708         clearTimeout(showProc);
33709         var t = e.getTarget();
33710         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33711             hideProc = setTimeout(hide, tm.hideDelay);
33712         }
33713     };
33714     
33715     var onMove = function(e){
33716         if(disabled){
33717             return;
33718         }
33719         xy = e.getXY();
33720         xy[1] += 18;
33721         if(tm.trackMouse && ce){
33722             el.setXY(xy);
33723         }
33724     };
33725     
33726     var onDown = function(e){
33727         clearTimeout(showProc);
33728         clearTimeout(hideProc);
33729         if(!e.within(el)){
33730             if(tm.hideOnClick){
33731                 hide();
33732                 tm.disable();
33733                 tm.enable.defer(100, tm);
33734             }
33735         }
33736     };
33737     
33738     var getPad = function(){
33739         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33740     };
33741
33742     var show = function(o){
33743         if(disabled){
33744             return;
33745         }
33746         clearTimeout(dismissProc);
33747         ce = o;
33748         if(removeCls){ // in case manually hidden
33749             el.removeClass(removeCls);
33750             removeCls = null;
33751         }
33752         if(ce.cls){
33753             el.addClass(ce.cls);
33754             removeCls = ce.cls;
33755         }
33756         if(ce.title){
33757             tipTitle.update(ce.title);
33758             tipTitle.show();
33759         }else{
33760             tipTitle.update('');
33761             tipTitle.hide();
33762         }
33763         el.dom.style.width  = tm.maxWidth+'px';
33764         //tipBody.dom.style.width = '';
33765         tipBodyText.update(o.text);
33766         var p = getPad(), w = ce.width;
33767         if(!w){
33768             var td = tipBodyText.dom;
33769             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33770             if(aw > tm.maxWidth){
33771                 w = tm.maxWidth;
33772             }else if(aw < tm.minWidth){
33773                 w = tm.minWidth;
33774             }else{
33775                 w = aw;
33776             }
33777         }
33778         //tipBody.setWidth(w);
33779         el.setWidth(parseInt(w, 10) + p);
33780         if(ce.autoHide === false){
33781             close.setDisplayed(true);
33782             if(dd){
33783                 dd.unlock();
33784             }
33785         }else{
33786             close.setDisplayed(false);
33787             if(dd){
33788                 dd.lock();
33789             }
33790         }
33791         if(xy){
33792             el.avoidY = xy[1]-18;
33793             el.setXY(xy);
33794         }
33795         if(tm.animate){
33796             el.setOpacity(.1);
33797             el.setStyle("visibility", "visible");
33798             el.fadeIn({callback: afterShow});
33799         }else{
33800             afterShow();
33801         }
33802     };
33803     
33804     var afterShow = function(){
33805         if(ce){
33806             el.show();
33807             esc.enable();
33808             if(tm.autoDismiss && ce.autoHide !== false){
33809                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33810             }
33811         }
33812     };
33813     
33814     var hide = function(noanim){
33815         clearTimeout(dismissProc);
33816         clearTimeout(hideProc);
33817         ce = null;
33818         if(el.isVisible()){
33819             esc.disable();
33820             if(noanim !== true && tm.animate){
33821                 el.fadeOut({callback: afterHide});
33822             }else{
33823                 afterHide();
33824             } 
33825         }
33826     };
33827     
33828     var afterHide = function(){
33829         el.hide();
33830         if(removeCls){
33831             el.removeClass(removeCls);
33832             removeCls = null;
33833         }
33834     };
33835     
33836     return {
33837         /**
33838         * @cfg {Number} minWidth
33839         * The minimum width of the quick tip (defaults to 40)
33840         */
33841        minWidth : 40,
33842         /**
33843         * @cfg {Number} maxWidth
33844         * The maximum width of the quick tip (defaults to 300)
33845         */
33846        maxWidth : 300,
33847         /**
33848         * @cfg {Boolean} interceptTitles
33849         * True to automatically use the element's DOM title value if available (defaults to false)
33850         */
33851        interceptTitles : false,
33852         /**
33853         * @cfg {Boolean} trackMouse
33854         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33855         */
33856        trackMouse : false,
33857         /**
33858         * @cfg {Boolean} hideOnClick
33859         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33860         */
33861        hideOnClick : true,
33862         /**
33863         * @cfg {Number} showDelay
33864         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33865         */
33866        showDelay : 500,
33867         /**
33868         * @cfg {Number} hideDelay
33869         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33870         */
33871        hideDelay : 200,
33872         /**
33873         * @cfg {Boolean} autoHide
33874         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33875         * Used in conjunction with hideDelay.
33876         */
33877        autoHide : true,
33878         /**
33879         * @cfg {Boolean}
33880         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33881         * (defaults to true).  Used in conjunction with autoDismissDelay.
33882         */
33883        autoDismiss : true,
33884         /**
33885         * @cfg {Number}
33886         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33887         */
33888        autoDismissDelay : 5000,
33889        /**
33890         * @cfg {Boolean} animate
33891         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33892         */
33893        animate : false,
33894
33895        /**
33896         * @cfg {String} title
33897         * Title text to display (defaults to '').  This can be any valid HTML markup.
33898         */
33899         title: '',
33900        /**
33901         * @cfg {String} text
33902         * Body text to display (defaults to '').  This can be any valid HTML markup.
33903         */
33904         text : '',
33905        /**
33906         * @cfg {String} cls
33907         * A CSS class to apply to the base quick tip element (defaults to '').
33908         */
33909         cls : '',
33910        /**
33911         * @cfg {Number} width
33912         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33913         * minWidth or maxWidth.
33914         */
33915         width : null,
33916
33917     /**
33918      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33919      * or display QuickTips in a page.
33920      */
33921        init : function(){
33922           tm = Roo.QuickTips;
33923           cfg = tm.tagConfig;
33924           if(!inited){
33925               if(!Roo.isReady){ // allow calling of init() before onReady
33926                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33927                   return;
33928               }
33929               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33930               el.fxDefaults = {stopFx: true};
33931               // maximum custom styling
33932               //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>');
33933               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>');              
33934               tipTitle = el.child('h3');
33935               tipTitle.enableDisplayMode("block");
33936               tipBody = el.child('div.x-tip-bd');
33937               tipBodyText = el.child('div.x-tip-bd-inner');
33938               //bdLeft = el.child('div.x-tip-bd-left');
33939               //bdRight = el.child('div.x-tip-bd-right');
33940               close = el.child('div.x-tip-close');
33941               close.enableDisplayMode("block");
33942               close.on("click", hide);
33943               var d = Roo.get(document);
33944               d.on("mousedown", onDown);
33945               d.on("mouseover", onOver);
33946               d.on("mouseout", onOut);
33947               d.on("mousemove", onMove);
33948               esc = d.addKeyListener(27, hide);
33949               esc.disable();
33950               if(Roo.dd.DD){
33951                   dd = el.initDD("default", null, {
33952                       onDrag : function(){
33953                           el.sync();  
33954                       }
33955                   });
33956                   dd.setHandleElId(tipTitle.id);
33957                   dd.lock();
33958               }
33959               inited = true;
33960           }
33961           this.enable(); 
33962        },
33963
33964     /**
33965      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33966      * are supported:
33967      * <pre>
33968 Property    Type                   Description
33969 ----------  ---------------------  ------------------------------------------------------------------------
33970 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33971      * </ul>
33972      * @param {Object} config The config object
33973      */
33974        register : function(config){
33975            var cs = config instanceof Array ? config : arguments;
33976            for(var i = 0, len = cs.length; i < len; i++) {
33977                var c = cs[i];
33978                var target = c.target;
33979                if(target){
33980                    if(target instanceof Array){
33981                        for(var j = 0, jlen = target.length; j < jlen; j++){
33982                            tagEls[target[j]] = c;
33983                        }
33984                    }else{
33985                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33986                    }
33987                }
33988            }
33989        },
33990
33991     /**
33992      * Removes this quick tip from its element and destroys it.
33993      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33994      */
33995        unregister : function(el){
33996            delete tagEls[Roo.id(el)];
33997        },
33998
33999     /**
34000      * Enable this quick tip.
34001      */
34002        enable : function(){
34003            if(inited && disabled){
34004                locks.pop();
34005                if(locks.length < 1){
34006                    disabled = false;
34007                }
34008            }
34009        },
34010
34011     /**
34012      * Disable this quick tip.
34013      */
34014        disable : function(){
34015           disabled = true;
34016           clearTimeout(showProc);
34017           clearTimeout(hideProc);
34018           clearTimeout(dismissProc);
34019           if(ce){
34020               hide(true);
34021           }
34022           locks.push(1);
34023        },
34024
34025     /**
34026      * Returns true if the quick tip is enabled, else false.
34027      */
34028        isEnabled : function(){
34029             return !disabled;
34030        },
34031
34032         // private
34033        tagConfig : {
34034            namespace : "roo", // was ext?? this may break..
34035            alt_namespace : "ext",
34036            attribute : "qtip",
34037            width : "width",
34038            target : "target",
34039            title : "qtitle",
34040            hide : "hide",
34041            cls : "qclass"
34042        }
34043    };
34044 }();
34045
34046 // backwards compat
34047 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34048  * Based on:
34049  * Ext JS Library 1.1.1
34050  * Copyright(c) 2006-2007, Ext JS, LLC.
34051  *
34052  * Originally Released Under LGPL - original licence link has changed is not relivant.
34053  *
34054  * Fork - LGPL
34055  * <script type="text/javascript">
34056  */
34057  
34058
34059 /**
34060  * @class Roo.tree.TreePanel
34061  * @extends Roo.data.Tree
34062
34063  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34064  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34065  * @cfg {Boolean} enableDD true to enable drag and drop
34066  * @cfg {Boolean} enableDrag true to enable just drag
34067  * @cfg {Boolean} enableDrop true to enable just drop
34068  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34069  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34070  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34071  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34072  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34073  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34074  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34075  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34076  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34077  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34078  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34079  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34080  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34081  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34082  * @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>
34083  * @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>
34084  * 
34085  * @constructor
34086  * @param {String/HTMLElement/Element} el The container element
34087  * @param {Object} config
34088  */
34089 Roo.tree.TreePanel = function(el, config){
34090     var root = false;
34091     var loader = false;
34092     if (config.root) {
34093         root = config.root;
34094         delete config.root;
34095     }
34096     if (config.loader) {
34097         loader = config.loader;
34098         delete config.loader;
34099     }
34100     
34101     Roo.apply(this, config);
34102     Roo.tree.TreePanel.superclass.constructor.call(this);
34103     this.el = Roo.get(el);
34104     this.el.addClass('x-tree');
34105     //console.log(root);
34106     if (root) {
34107         this.setRootNode( Roo.factory(root, Roo.tree));
34108     }
34109     if (loader) {
34110         this.loader = Roo.factory(loader, Roo.tree);
34111     }
34112    /**
34113     * Read-only. The id of the container element becomes this TreePanel's id.
34114     */
34115     this.id = this.el.id;
34116     this.addEvents({
34117         /**
34118         * @event beforeload
34119         * Fires before a node is loaded, return false to cancel
34120         * @param {Node} node The node being loaded
34121         */
34122         "beforeload" : true,
34123         /**
34124         * @event load
34125         * Fires when a node is loaded
34126         * @param {Node} node The node that was loaded
34127         */
34128         "load" : true,
34129         /**
34130         * @event textchange
34131         * Fires when the text for a node is changed
34132         * @param {Node} node The node
34133         * @param {String} text The new text
34134         * @param {String} oldText The old text
34135         */
34136         "textchange" : true,
34137         /**
34138         * @event beforeexpand
34139         * Fires before a node is expanded, return false to cancel.
34140         * @param {Node} node The node
34141         * @param {Boolean} deep
34142         * @param {Boolean} anim
34143         */
34144         "beforeexpand" : true,
34145         /**
34146         * @event beforecollapse
34147         * Fires before a node is collapsed, return false to cancel.
34148         * @param {Node} node The node
34149         * @param {Boolean} deep
34150         * @param {Boolean} anim
34151         */
34152         "beforecollapse" : true,
34153         /**
34154         * @event expand
34155         * Fires when a node is expanded
34156         * @param {Node} node The node
34157         */
34158         "expand" : true,
34159         /**
34160         * @event disabledchange
34161         * Fires when the disabled status of a node changes
34162         * @param {Node} node The node
34163         * @param {Boolean} disabled
34164         */
34165         "disabledchange" : true,
34166         /**
34167         * @event collapse
34168         * Fires when a node is collapsed
34169         * @param {Node} node The node
34170         */
34171         "collapse" : true,
34172         /**
34173         * @event beforeclick
34174         * Fires before click processing on a node. Return false to cancel the default action.
34175         * @param {Node} node The node
34176         * @param {Roo.EventObject} e The event object
34177         */
34178         "beforeclick":true,
34179         /**
34180         * @event checkchange
34181         * Fires when a node with a checkbox's checked property changes
34182         * @param {Node} this This node
34183         * @param {Boolean} checked
34184         */
34185         "checkchange":true,
34186         /**
34187         * @event click
34188         * Fires when a node is clicked
34189         * @param {Node} node The node
34190         * @param {Roo.EventObject} e The event object
34191         */
34192         "click":true,
34193         /**
34194         * @event dblclick
34195         * Fires when a node is double clicked
34196         * @param {Node} node The node
34197         * @param {Roo.EventObject} e The event object
34198         */
34199         "dblclick":true,
34200         /**
34201         * @event contextmenu
34202         * Fires when a node is right clicked
34203         * @param {Node} node The node
34204         * @param {Roo.EventObject} e The event object
34205         */
34206         "contextmenu":true,
34207         /**
34208         * @event beforechildrenrendered
34209         * Fires right before the child nodes for a node are rendered
34210         * @param {Node} node The node
34211         */
34212         "beforechildrenrendered":true,
34213         /**
34214         * @event startdrag
34215         * Fires when a node starts being dragged
34216         * @param {Roo.tree.TreePanel} this
34217         * @param {Roo.tree.TreeNode} node
34218         * @param {event} e The raw browser event
34219         */ 
34220        "startdrag" : true,
34221        /**
34222         * @event enddrag
34223         * Fires when a drag operation is complete
34224         * @param {Roo.tree.TreePanel} this
34225         * @param {Roo.tree.TreeNode} node
34226         * @param {event} e The raw browser event
34227         */
34228        "enddrag" : true,
34229        /**
34230         * @event dragdrop
34231         * Fires when a dragged node is dropped on a valid DD target
34232         * @param {Roo.tree.TreePanel} this
34233         * @param {Roo.tree.TreeNode} node
34234         * @param {DD} dd The dd it was dropped on
34235         * @param {event} e The raw browser event
34236         */
34237        "dragdrop" : true,
34238        /**
34239         * @event beforenodedrop
34240         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34241         * passed to handlers has the following properties:<br />
34242         * <ul style="padding:5px;padding-left:16px;">
34243         * <li>tree - The TreePanel</li>
34244         * <li>target - The node being targeted for the drop</li>
34245         * <li>data - The drag data from the drag source</li>
34246         * <li>point - The point of the drop - append, above or below</li>
34247         * <li>source - The drag source</li>
34248         * <li>rawEvent - Raw mouse event</li>
34249         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34250         * to be inserted by setting them on this object.</li>
34251         * <li>cancel - Set this to true to cancel the drop.</li>
34252         * </ul>
34253         * @param {Object} dropEvent
34254         */
34255        "beforenodedrop" : true,
34256        /**
34257         * @event nodedrop
34258         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34259         * passed to handlers has the following properties:<br />
34260         * <ul style="padding:5px;padding-left:16px;">
34261         * <li>tree - The TreePanel</li>
34262         * <li>target - The node being targeted for the drop</li>
34263         * <li>data - The drag data from the drag source</li>
34264         * <li>point - The point of the drop - append, above or below</li>
34265         * <li>source - The drag source</li>
34266         * <li>rawEvent - Raw mouse event</li>
34267         * <li>dropNode - Dropped node(s).</li>
34268         * </ul>
34269         * @param {Object} dropEvent
34270         */
34271        "nodedrop" : true,
34272         /**
34273         * @event nodedragover
34274         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34275         * passed to handlers has the following properties:<br />
34276         * <ul style="padding:5px;padding-left:16px;">
34277         * <li>tree - The TreePanel</li>
34278         * <li>target - The node being targeted for the drop</li>
34279         * <li>data - The drag data from the drag source</li>
34280         * <li>point - The point of the drop - append, above or below</li>
34281         * <li>source - The drag source</li>
34282         * <li>rawEvent - Raw mouse event</li>
34283         * <li>dropNode - Drop node(s) provided by the source.</li>
34284         * <li>cancel - Set this to true to signal drop not allowed.</li>
34285         * </ul>
34286         * @param {Object} dragOverEvent
34287         */
34288        "nodedragover" : true,
34289        /**
34290         * @event appendnode
34291         * Fires when append node to the tree
34292         * @param {Roo.tree.TreePanel} this
34293         * @param {Roo.tree.TreeNode} node
34294         * @param {Number} index The index of the newly appended node
34295         */
34296        "appendnode" : true
34297         
34298     });
34299     if(this.singleExpand){
34300        this.on("beforeexpand", this.restrictExpand, this);
34301     }
34302     if (this.editor) {
34303         this.editor.tree = this;
34304         this.editor = Roo.factory(this.editor, Roo.tree);
34305     }
34306     
34307     if (this.selModel) {
34308         this.selModel = Roo.factory(this.selModel, Roo.tree);
34309     }
34310    
34311 };
34312 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34313     rootVisible : true,
34314     animate: Roo.enableFx,
34315     lines : true,
34316     enableDD : false,
34317     hlDrop : Roo.enableFx,
34318   
34319     renderer: false,
34320     
34321     rendererTip: false,
34322     // private
34323     restrictExpand : function(node){
34324         var p = node.parentNode;
34325         if(p){
34326             if(p.expandedChild && p.expandedChild.parentNode == p){
34327                 p.expandedChild.collapse();
34328             }
34329             p.expandedChild = node;
34330         }
34331     },
34332
34333     // private override
34334     setRootNode : function(node){
34335         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34336         if(!this.rootVisible){
34337             node.ui = new Roo.tree.RootTreeNodeUI(node);
34338         }
34339         return node;
34340     },
34341
34342     /**
34343      * Returns the container element for this TreePanel
34344      */
34345     getEl : function(){
34346         return this.el;
34347     },
34348
34349     /**
34350      * Returns the default TreeLoader for this TreePanel
34351      */
34352     getLoader : function(){
34353         return this.loader;
34354     },
34355
34356     /**
34357      * Expand all nodes
34358      */
34359     expandAll : function(){
34360         this.root.expand(true);
34361     },
34362
34363     /**
34364      * Collapse all nodes
34365      */
34366     collapseAll : function(){
34367         this.root.collapse(true);
34368     },
34369
34370     /**
34371      * Returns the selection model used by this TreePanel
34372      */
34373     getSelectionModel : function(){
34374         if(!this.selModel){
34375             this.selModel = new Roo.tree.DefaultSelectionModel();
34376         }
34377         return this.selModel;
34378     },
34379
34380     /**
34381      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34382      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34383      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34384      * @return {Array}
34385      */
34386     getChecked : function(a, startNode){
34387         startNode = startNode || this.root;
34388         var r = [];
34389         var f = function(){
34390             if(this.attributes.checked){
34391                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34392             }
34393         }
34394         startNode.cascade(f);
34395         return r;
34396     },
34397
34398     /**
34399      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34400      * @param {String} path
34401      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34402      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34403      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34404      */
34405     expandPath : function(path, attr, callback){
34406         attr = attr || "id";
34407         var keys = path.split(this.pathSeparator);
34408         var curNode = this.root;
34409         if(curNode.attributes[attr] != keys[1]){ // invalid root
34410             if(callback){
34411                 callback(false, null);
34412             }
34413             return;
34414         }
34415         var index = 1;
34416         var f = function(){
34417             if(++index == keys.length){
34418                 if(callback){
34419                     callback(true, curNode);
34420                 }
34421                 return;
34422             }
34423             var c = curNode.findChild(attr, keys[index]);
34424             if(!c){
34425                 if(callback){
34426                     callback(false, curNode);
34427                 }
34428                 return;
34429             }
34430             curNode = c;
34431             c.expand(false, false, f);
34432         };
34433         curNode.expand(false, false, f);
34434     },
34435
34436     /**
34437      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34438      * @param {String} path
34439      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34440      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34441      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34442      */
34443     selectPath : function(path, attr, callback){
34444         attr = attr || "id";
34445         var keys = path.split(this.pathSeparator);
34446         var v = keys.pop();
34447         if(keys.length > 0){
34448             var f = function(success, node){
34449                 if(success && node){
34450                     var n = node.findChild(attr, v);
34451                     if(n){
34452                         n.select();
34453                         if(callback){
34454                             callback(true, n);
34455                         }
34456                     }else if(callback){
34457                         callback(false, n);
34458                     }
34459                 }else{
34460                     if(callback){
34461                         callback(false, n);
34462                     }
34463                 }
34464             };
34465             this.expandPath(keys.join(this.pathSeparator), attr, f);
34466         }else{
34467             this.root.select();
34468             if(callback){
34469                 callback(true, this.root);
34470             }
34471         }
34472     },
34473
34474     getTreeEl : function(){
34475         return this.el;
34476     },
34477
34478     /**
34479      * Trigger rendering of this TreePanel
34480      */
34481     render : function(){
34482         if (this.innerCt) {
34483             return this; // stop it rendering more than once!!
34484         }
34485         
34486         this.innerCt = this.el.createChild({tag:"ul",
34487                cls:"x-tree-root-ct " +
34488                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34489
34490         if(this.containerScroll){
34491             Roo.dd.ScrollManager.register(this.el);
34492         }
34493         if((this.enableDD || this.enableDrop) && !this.dropZone){
34494            /**
34495             * The dropZone used by this tree if drop is enabled
34496             * @type Roo.tree.TreeDropZone
34497             */
34498              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34499                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34500            });
34501         }
34502         if((this.enableDD || this.enableDrag) && !this.dragZone){
34503            /**
34504             * The dragZone used by this tree if drag is enabled
34505             * @type Roo.tree.TreeDragZone
34506             */
34507             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34508                ddGroup: this.ddGroup || "TreeDD",
34509                scroll: this.ddScroll
34510            });
34511         }
34512         this.getSelectionModel().init(this);
34513         if (!this.root) {
34514             Roo.log("ROOT not set in tree");
34515             return this;
34516         }
34517         this.root.render();
34518         if(!this.rootVisible){
34519             this.root.renderChildren();
34520         }
34521         return this;
34522     }
34523 });/*
34524  * Based on:
34525  * Ext JS Library 1.1.1
34526  * Copyright(c) 2006-2007, Ext JS, LLC.
34527  *
34528  * Originally Released Under LGPL - original licence link has changed is not relivant.
34529  *
34530  * Fork - LGPL
34531  * <script type="text/javascript">
34532  */
34533  
34534
34535 /**
34536  * @class Roo.tree.DefaultSelectionModel
34537  * @extends Roo.util.Observable
34538  * The default single selection for a TreePanel.
34539  * @param {Object} cfg Configuration
34540  */
34541 Roo.tree.DefaultSelectionModel = function(cfg){
34542    this.selNode = null;
34543    
34544    
34545    
34546    this.addEvents({
34547        /**
34548         * @event selectionchange
34549         * Fires when the selected node changes
34550         * @param {DefaultSelectionModel} this
34551         * @param {TreeNode} node the new selection
34552         */
34553        "selectionchange" : true,
34554
34555        /**
34556         * @event beforeselect
34557         * Fires before the selected node changes, return false to cancel the change
34558         * @param {DefaultSelectionModel} this
34559         * @param {TreeNode} node the new selection
34560         * @param {TreeNode} node the old selection
34561         */
34562        "beforeselect" : true
34563    });
34564    
34565     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34566 };
34567
34568 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34569     init : function(tree){
34570         this.tree = tree;
34571         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34572         tree.on("click", this.onNodeClick, this);
34573     },
34574     
34575     onNodeClick : function(node, e){
34576         if (e.ctrlKey && this.selNode == node)  {
34577             this.unselect(node);
34578             return;
34579         }
34580         this.select(node);
34581     },
34582     
34583     /**
34584      * Select a node.
34585      * @param {TreeNode} node The node to select
34586      * @return {TreeNode} The selected node
34587      */
34588     select : function(node){
34589         var last = this.selNode;
34590         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34591             if(last){
34592                 last.ui.onSelectedChange(false);
34593             }
34594             this.selNode = node;
34595             node.ui.onSelectedChange(true);
34596             this.fireEvent("selectionchange", this, node, last);
34597         }
34598         return node;
34599     },
34600     
34601     /**
34602      * Deselect a node.
34603      * @param {TreeNode} node The node to unselect
34604      */
34605     unselect : function(node){
34606         if(this.selNode == node){
34607             this.clearSelections();
34608         }    
34609     },
34610     
34611     /**
34612      * Clear all selections
34613      */
34614     clearSelections : function(){
34615         var n = this.selNode;
34616         if(n){
34617             n.ui.onSelectedChange(false);
34618             this.selNode = null;
34619             this.fireEvent("selectionchange", this, null);
34620         }
34621         return n;
34622     },
34623     
34624     /**
34625      * Get the selected node
34626      * @return {TreeNode} The selected node
34627      */
34628     getSelectedNode : function(){
34629         return this.selNode;    
34630     },
34631     
34632     /**
34633      * Returns true if the node is selected
34634      * @param {TreeNode} node The node to check
34635      * @return {Boolean}
34636      */
34637     isSelected : function(node){
34638         return this.selNode == node;  
34639     },
34640
34641     /**
34642      * Selects the node above the selected node in the tree, intelligently walking the nodes
34643      * @return TreeNode The new selection
34644      */
34645     selectPrevious : function(){
34646         var s = this.selNode || this.lastSelNode;
34647         if(!s){
34648             return null;
34649         }
34650         var ps = s.previousSibling;
34651         if(ps){
34652             if(!ps.isExpanded() || ps.childNodes.length < 1){
34653                 return this.select(ps);
34654             } else{
34655                 var lc = ps.lastChild;
34656                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34657                     lc = lc.lastChild;
34658                 }
34659                 return this.select(lc);
34660             }
34661         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34662             return this.select(s.parentNode);
34663         }
34664         return null;
34665     },
34666
34667     /**
34668      * Selects the node above the selected node in the tree, intelligently walking the nodes
34669      * @return TreeNode The new selection
34670      */
34671     selectNext : function(){
34672         var s = this.selNode || this.lastSelNode;
34673         if(!s){
34674             return null;
34675         }
34676         if(s.firstChild && s.isExpanded()){
34677              return this.select(s.firstChild);
34678          }else if(s.nextSibling){
34679              return this.select(s.nextSibling);
34680          }else if(s.parentNode){
34681             var newS = null;
34682             s.parentNode.bubble(function(){
34683                 if(this.nextSibling){
34684                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34685                     return false;
34686                 }
34687             });
34688             return newS;
34689          }
34690         return null;
34691     },
34692
34693     onKeyDown : function(e){
34694         var s = this.selNode || this.lastSelNode;
34695         // undesirable, but required
34696         var sm = this;
34697         if(!s){
34698             return;
34699         }
34700         var k = e.getKey();
34701         switch(k){
34702              case e.DOWN:
34703                  e.stopEvent();
34704                  this.selectNext();
34705              break;
34706              case e.UP:
34707                  e.stopEvent();
34708                  this.selectPrevious();
34709              break;
34710              case e.RIGHT:
34711                  e.preventDefault();
34712                  if(s.hasChildNodes()){
34713                      if(!s.isExpanded()){
34714                          s.expand();
34715                      }else if(s.firstChild){
34716                          this.select(s.firstChild, e);
34717                      }
34718                  }
34719              break;
34720              case e.LEFT:
34721                  e.preventDefault();
34722                  if(s.hasChildNodes() && s.isExpanded()){
34723                      s.collapse();
34724                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34725                      this.select(s.parentNode, e);
34726                  }
34727              break;
34728         };
34729     }
34730 });
34731
34732 /**
34733  * @class Roo.tree.MultiSelectionModel
34734  * @extends Roo.util.Observable
34735  * Multi selection for a TreePanel.
34736  * @param {Object} cfg Configuration
34737  */
34738 Roo.tree.MultiSelectionModel = function(){
34739    this.selNodes = [];
34740    this.selMap = {};
34741    this.addEvents({
34742        /**
34743         * @event selectionchange
34744         * Fires when the selected nodes change
34745         * @param {MultiSelectionModel} this
34746         * @param {Array} nodes Array of the selected nodes
34747         */
34748        "selectionchange" : true
34749    });
34750    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34751    
34752 };
34753
34754 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34755     init : function(tree){
34756         this.tree = tree;
34757         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34758         tree.on("click", this.onNodeClick, this);
34759     },
34760     
34761     onNodeClick : function(node, e){
34762         this.select(node, e, e.ctrlKey);
34763     },
34764     
34765     /**
34766      * Select a node.
34767      * @param {TreeNode} node The node to select
34768      * @param {EventObject} e (optional) An event associated with the selection
34769      * @param {Boolean} keepExisting True to retain existing selections
34770      * @return {TreeNode} The selected node
34771      */
34772     select : function(node, e, keepExisting){
34773         if(keepExisting !== true){
34774             this.clearSelections(true);
34775         }
34776         if(this.isSelected(node)){
34777             this.lastSelNode = node;
34778             return node;
34779         }
34780         this.selNodes.push(node);
34781         this.selMap[node.id] = node;
34782         this.lastSelNode = node;
34783         node.ui.onSelectedChange(true);
34784         this.fireEvent("selectionchange", this, this.selNodes);
34785         return node;
34786     },
34787     
34788     /**
34789      * Deselect a node.
34790      * @param {TreeNode} node The node to unselect
34791      */
34792     unselect : function(node){
34793         if(this.selMap[node.id]){
34794             node.ui.onSelectedChange(false);
34795             var sn = this.selNodes;
34796             var index = -1;
34797             if(sn.indexOf){
34798                 index = sn.indexOf(node);
34799             }else{
34800                 for(var i = 0, len = sn.length; i < len; i++){
34801                     if(sn[i] == node){
34802                         index = i;
34803                         break;
34804                     }
34805                 }
34806             }
34807             if(index != -1){
34808                 this.selNodes.splice(index, 1);
34809             }
34810             delete this.selMap[node.id];
34811             this.fireEvent("selectionchange", this, this.selNodes);
34812         }
34813     },
34814     
34815     /**
34816      * Clear all selections
34817      */
34818     clearSelections : function(suppressEvent){
34819         var sn = this.selNodes;
34820         if(sn.length > 0){
34821             for(var i = 0, len = sn.length; i < len; i++){
34822                 sn[i].ui.onSelectedChange(false);
34823             }
34824             this.selNodes = [];
34825             this.selMap = {};
34826             if(suppressEvent !== true){
34827                 this.fireEvent("selectionchange", this, this.selNodes);
34828             }
34829         }
34830     },
34831     
34832     /**
34833      * Returns true if the node is selected
34834      * @param {TreeNode} node The node to check
34835      * @return {Boolean}
34836      */
34837     isSelected : function(node){
34838         return this.selMap[node.id] ? true : false;  
34839     },
34840     
34841     /**
34842      * Returns an array of the selected nodes
34843      * @return {Array}
34844      */
34845     getSelectedNodes : function(){
34846         return this.selNodes;    
34847     },
34848
34849     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34850
34851     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34852
34853     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34854 });/*
34855  * Based on:
34856  * Ext JS Library 1.1.1
34857  * Copyright(c) 2006-2007, Ext JS, LLC.
34858  *
34859  * Originally Released Under LGPL - original licence link has changed is not relivant.
34860  *
34861  * Fork - LGPL
34862  * <script type="text/javascript">
34863  */
34864  
34865 /**
34866  * @class Roo.tree.TreeNode
34867  * @extends Roo.data.Node
34868  * @cfg {String} text The text for this node
34869  * @cfg {Boolean} expanded true to start the node expanded
34870  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34871  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34872  * @cfg {Boolean} disabled true to start the node disabled
34873  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34874  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34875  * @cfg {String} cls A css class to be added to the node
34876  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34877  * @cfg {String} href URL of the link used for the node (defaults to #)
34878  * @cfg {String} hrefTarget target frame for the link
34879  * @cfg {String} qtip An Ext QuickTip for the node
34880  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34881  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34882  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34883  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34884  * (defaults to undefined with no checkbox rendered)
34885  * @constructor
34886  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34887  */
34888 Roo.tree.TreeNode = function(attributes){
34889     attributes = attributes || {};
34890     if(typeof attributes == "string"){
34891         attributes = {text: attributes};
34892     }
34893     this.childrenRendered = false;
34894     this.rendered = false;
34895     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34896     this.expanded = attributes.expanded === true;
34897     this.isTarget = attributes.isTarget !== false;
34898     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34899     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34900
34901     /**
34902      * Read-only. The text for this node. To change it use setText().
34903      * @type String
34904      */
34905     this.text = attributes.text;
34906     /**
34907      * True if this node is disabled.
34908      * @type Boolean
34909      */
34910     this.disabled = attributes.disabled === true;
34911
34912     this.addEvents({
34913         /**
34914         * @event textchange
34915         * Fires when the text for this node is changed
34916         * @param {Node} this This node
34917         * @param {String} text The new text
34918         * @param {String} oldText The old text
34919         */
34920         "textchange" : true,
34921         /**
34922         * @event beforeexpand
34923         * Fires before this node is expanded, return false to cancel.
34924         * @param {Node} this This node
34925         * @param {Boolean} deep
34926         * @param {Boolean} anim
34927         */
34928         "beforeexpand" : true,
34929         /**
34930         * @event beforecollapse
34931         * Fires before this node is collapsed, return false to cancel.
34932         * @param {Node} this This node
34933         * @param {Boolean} deep
34934         * @param {Boolean} anim
34935         */
34936         "beforecollapse" : true,
34937         /**
34938         * @event expand
34939         * Fires when this node is expanded
34940         * @param {Node} this This node
34941         */
34942         "expand" : true,
34943         /**
34944         * @event disabledchange
34945         * Fires when the disabled status of this node changes
34946         * @param {Node} this This node
34947         * @param {Boolean} disabled
34948         */
34949         "disabledchange" : true,
34950         /**
34951         * @event collapse
34952         * Fires when this node is collapsed
34953         * @param {Node} this This node
34954         */
34955         "collapse" : true,
34956         /**
34957         * @event beforeclick
34958         * Fires before click processing. Return false to cancel the default action.
34959         * @param {Node} this This node
34960         * @param {Roo.EventObject} e The event object
34961         */
34962         "beforeclick":true,
34963         /**
34964         * @event checkchange
34965         * Fires when a node with a checkbox's checked property changes
34966         * @param {Node} this This node
34967         * @param {Boolean} checked
34968         */
34969         "checkchange":true,
34970         /**
34971         * @event click
34972         * Fires when this node is clicked
34973         * @param {Node} this This node
34974         * @param {Roo.EventObject} e The event object
34975         */
34976         "click":true,
34977         /**
34978         * @event dblclick
34979         * Fires when this node is double clicked
34980         * @param {Node} this This node
34981         * @param {Roo.EventObject} e The event object
34982         */
34983         "dblclick":true,
34984         /**
34985         * @event contextmenu
34986         * Fires when this node is right clicked
34987         * @param {Node} this This node
34988         * @param {Roo.EventObject} e The event object
34989         */
34990         "contextmenu":true,
34991         /**
34992         * @event beforechildrenrendered
34993         * Fires right before the child nodes for this node are rendered
34994         * @param {Node} this This node
34995         */
34996         "beforechildrenrendered":true
34997     });
34998
34999     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35000
35001     /**
35002      * Read-only. The UI for this node
35003      * @type TreeNodeUI
35004      */
35005     this.ui = new uiClass(this);
35006     
35007     // finally support items[]
35008     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35009         return;
35010     }
35011     
35012     
35013     Roo.each(this.attributes.items, function(c) {
35014         this.appendChild(Roo.factory(c,Roo.Tree));
35015     }, this);
35016     delete this.attributes.items;
35017     
35018     
35019     
35020 };
35021 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35022     preventHScroll: true,
35023     /**
35024      * Returns true if this node is expanded
35025      * @return {Boolean}
35026      */
35027     isExpanded : function(){
35028         return this.expanded;
35029     },
35030
35031     /**
35032      * Returns the UI object for this node
35033      * @return {TreeNodeUI}
35034      */
35035     getUI : function(){
35036         return this.ui;
35037     },
35038
35039     // private override
35040     setFirstChild : function(node){
35041         var of = this.firstChild;
35042         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35043         if(this.childrenRendered && of && node != of){
35044             of.renderIndent(true, true);
35045         }
35046         if(this.rendered){
35047             this.renderIndent(true, true);
35048         }
35049     },
35050
35051     // private override
35052     setLastChild : function(node){
35053         var ol = this.lastChild;
35054         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35055         if(this.childrenRendered && ol && node != ol){
35056             ol.renderIndent(true, true);
35057         }
35058         if(this.rendered){
35059             this.renderIndent(true, true);
35060         }
35061     },
35062
35063     // these methods are overridden to provide lazy rendering support
35064     // private override
35065     appendChild : function()
35066     {
35067         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35068         if(node && this.childrenRendered){
35069             node.render();
35070         }
35071         this.ui.updateExpandIcon();
35072         return node;
35073     },
35074
35075     // private override
35076     removeChild : function(node){
35077         this.ownerTree.getSelectionModel().unselect(node);
35078         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35079         // if it's been rendered remove dom node
35080         if(this.childrenRendered){
35081             node.ui.remove();
35082         }
35083         if(this.childNodes.length < 1){
35084             this.collapse(false, false);
35085         }else{
35086             this.ui.updateExpandIcon();
35087         }
35088         if(!this.firstChild) {
35089             this.childrenRendered = false;
35090         }
35091         return node;
35092     },
35093
35094     // private override
35095     insertBefore : function(node, refNode){
35096         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35097         if(newNode && refNode && this.childrenRendered){
35098             node.render();
35099         }
35100         this.ui.updateExpandIcon();
35101         return newNode;
35102     },
35103
35104     /**
35105      * Sets the text for this node
35106      * @param {String} text
35107      */
35108     setText : function(text){
35109         var oldText = this.text;
35110         this.text = text;
35111         this.attributes.text = text;
35112         if(this.rendered){ // event without subscribing
35113             this.ui.onTextChange(this, text, oldText);
35114         }
35115         this.fireEvent("textchange", this, text, oldText);
35116     },
35117
35118     /**
35119      * Triggers selection of this node
35120      */
35121     select : function(){
35122         this.getOwnerTree().getSelectionModel().select(this);
35123     },
35124
35125     /**
35126      * Triggers deselection of this node
35127      */
35128     unselect : function(){
35129         this.getOwnerTree().getSelectionModel().unselect(this);
35130     },
35131
35132     /**
35133      * Returns true if this node is selected
35134      * @return {Boolean}
35135      */
35136     isSelected : function(){
35137         return this.getOwnerTree().getSelectionModel().isSelected(this);
35138     },
35139
35140     /**
35141      * Expand this node.
35142      * @param {Boolean} deep (optional) True to expand all children as well
35143      * @param {Boolean} anim (optional) false to cancel the default animation
35144      * @param {Function} callback (optional) A callback to be called when
35145      * expanding this node completes (does not wait for deep expand to complete).
35146      * Called with 1 parameter, this node.
35147      */
35148     expand : function(deep, anim, callback){
35149         if(!this.expanded){
35150             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35151                 return;
35152             }
35153             if(!this.childrenRendered){
35154                 this.renderChildren();
35155             }
35156             this.expanded = true;
35157             
35158             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35159                 this.ui.animExpand(function(){
35160                     this.fireEvent("expand", this);
35161                     if(typeof callback == "function"){
35162                         callback(this);
35163                     }
35164                     if(deep === true){
35165                         this.expandChildNodes(true);
35166                     }
35167                 }.createDelegate(this));
35168                 return;
35169             }else{
35170                 this.ui.expand();
35171                 this.fireEvent("expand", this);
35172                 if(typeof callback == "function"){
35173                     callback(this);
35174                 }
35175             }
35176         }else{
35177            if(typeof callback == "function"){
35178                callback(this);
35179            }
35180         }
35181         if(deep === true){
35182             this.expandChildNodes(true);
35183         }
35184     },
35185
35186     isHiddenRoot : function(){
35187         return this.isRoot && !this.getOwnerTree().rootVisible;
35188     },
35189
35190     /**
35191      * Collapse this node.
35192      * @param {Boolean} deep (optional) True to collapse all children as well
35193      * @param {Boolean} anim (optional) false to cancel the default animation
35194      */
35195     collapse : function(deep, anim){
35196         if(this.expanded && !this.isHiddenRoot()){
35197             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35198                 return;
35199             }
35200             this.expanded = false;
35201             if((this.getOwnerTree().animate && anim !== false) || anim){
35202                 this.ui.animCollapse(function(){
35203                     this.fireEvent("collapse", this);
35204                     if(deep === true){
35205                         this.collapseChildNodes(true);
35206                     }
35207                 }.createDelegate(this));
35208                 return;
35209             }else{
35210                 this.ui.collapse();
35211                 this.fireEvent("collapse", this);
35212             }
35213         }
35214         if(deep === true){
35215             var cs = this.childNodes;
35216             for(var i = 0, len = cs.length; i < len; i++) {
35217                 cs[i].collapse(true, false);
35218             }
35219         }
35220     },
35221
35222     // private
35223     delayedExpand : function(delay){
35224         if(!this.expandProcId){
35225             this.expandProcId = this.expand.defer(delay, this);
35226         }
35227     },
35228
35229     // private
35230     cancelExpand : function(){
35231         if(this.expandProcId){
35232             clearTimeout(this.expandProcId);
35233         }
35234         this.expandProcId = false;
35235     },
35236
35237     /**
35238      * Toggles expanded/collapsed state of the node
35239      */
35240     toggle : function(){
35241         if(this.expanded){
35242             this.collapse();
35243         }else{
35244             this.expand();
35245         }
35246     },
35247
35248     /**
35249      * Ensures all parent nodes are expanded
35250      */
35251     ensureVisible : function(callback){
35252         var tree = this.getOwnerTree();
35253         tree.expandPath(this.parentNode.getPath(), false, function(){
35254             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35255             Roo.callback(callback);
35256         }.createDelegate(this));
35257     },
35258
35259     /**
35260      * Expand all child nodes
35261      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35262      */
35263     expandChildNodes : function(deep){
35264         var cs = this.childNodes;
35265         for(var i = 0, len = cs.length; i < len; i++) {
35266                 cs[i].expand(deep);
35267         }
35268     },
35269
35270     /**
35271      * Collapse all child nodes
35272      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35273      */
35274     collapseChildNodes : function(deep){
35275         var cs = this.childNodes;
35276         for(var i = 0, len = cs.length; i < len; i++) {
35277                 cs[i].collapse(deep);
35278         }
35279     },
35280
35281     /**
35282      * Disables this node
35283      */
35284     disable : function(){
35285         this.disabled = true;
35286         this.unselect();
35287         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35288             this.ui.onDisableChange(this, true);
35289         }
35290         this.fireEvent("disabledchange", this, true);
35291     },
35292
35293     /**
35294      * Enables this node
35295      */
35296     enable : function(){
35297         this.disabled = false;
35298         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35299             this.ui.onDisableChange(this, false);
35300         }
35301         this.fireEvent("disabledchange", this, false);
35302     },
35303
35304     // private
35305     renderChildren : function(suppressEvent){
35306         if(suppressEvent !== false){
35307             this.fireEvent("beforechildrenrendered", this);
35308         }
35309         var cs = this.childNodes;
35310         for(var i = 0, len = cs.length; i < len; i++){
35311             cs[i].render(true);
35312         }
35313         this.childrenRendered = true;
35314     },
35315
35316     // private
35317     sort : function(fn, scope){
35318         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35319         if(this.childrenRendered){
35320             var cs = this.childNodes;
35321             for(var i = 0, len = cs.length; i < len; i++){
35322                 cs[i].render(true);
35323             }
35324         }
35325     },
35326
35327     // private
35328     render : function(bulkRender){
35329         this.ui.render(bulkRender);
35330         if(!this.rendered){
35331             this.rendered = true;
35332             if(this.expanded){
35333                 this.expanded = false;
35334                 this.expand(false, false);
35335             }
35336         }
35337     },
35338
35339     // private
35340     renderIndent : function(deep, refresh){
35341         if(refresh){
35342             this.ui.childIndent = null;
35343         }
35344         this.ui.renderIndent();
35345         if(deep === true && this.childrenRendered){
35346             var cs = this.childNodes;
35347             for(var i = 0, len = cs.length; i < len; i++){
35348                 cs[i].renderIndent(true, refresh);
35349             }
35350         }
35351     }
35352 });/*
35353  * Based on:
35354  * Ext JS Library 1.1.1
35355  * Copyright(c) 2006-2007, Ext JS, LLC.
35356  *
35357  * Originally Released Under LGPL - original licence link has changed is not relivant.
35358  *
35359  * Fork - LGPL
35360  * <script type="text/javascript">
35361  */
35362  
35363 /**
35364  * @class Roo.tree.AsyncTreeNode
35365  * @extends Roo.tree.TreeNode
35366  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35367  * @constructor
35368  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35369  */
35370  Roo.tree.AsyncTreeNode = function(config){
35371     this.loaded = false;
35372     this.loading = false;
35373     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35374     /**
35375     * @event beforeload
35376     * Fires before this node is loaded, return false to cancel
35377     * @param {Node} this This node
35378     */
35379     this.addEvents({'beforeload':true, 'load': true});
35380     /**
35381     * @event load
35382     * Fires when this node is loaded
35383     * @param {Node} this This node
35384     */
35385     /**
35386      * The loader used by this node (defaults to using the tree's defined loader)
35387      * @type TreeLoader
35388      * @property loader
35389      */
35390 };
35391 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35392     expand : function(deep, anim, callback){
35393         if(this.loading){ // if an async load is already running, waiting til it's done
35394             var timer;
35395             var f = function(){
35396                 if(!this.loading){ // done loading
35397                     clearInterval(timer);
35398                     this.expand(deep, anim, callback);
35399                 }
35400             }.createDelegate(this);
35401             timer = setInterval(f, 200);
35402             return;
35403         }
35404         if(!this.loaded){
35405             if(this.fireEvent("beforeload", this) === false){
35406                 return;
35407             }
35408             this.loading = true;
35409             this.ui.beforeLoad(this);
35410             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35411             if(loader){
35412                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35413                 return;
35414             }
35415         }
35416         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35417     },
35418     
35419     /**
35420      * Returns true if this node is currently loading
35421      * @return {Boolean}
35422      */
35423     isLoading : function(){
35424         return this.loading;  
35425     },
35426     
35427     loadComplete : function(deep, anim, callback){
35428         this.loading = false;
35429         this.loaded = true;
35430         this.ui.afterLoad(this);
35431         this.fireEvent("load", this);
35432         this.expand(deep, anim, callback);
35433     },
35434     
35435     /**
35436      * Returns true if this node has been loaded
35437      * @return {Boolean}
35438      */
35439     isLoaded : function(){
35440         return this.loaded;
35441     },
35442     
35443     hasChildNodes : function(){
35444         if(!this.isLeaf() && !this.loaded){
35445             return true;
35446         }else{
35447             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35448         }
35449     },
35450
35451     /**
35452      * Trigger a reload for this node
35453      * @param {Function} callback
35454      */
35455     reload : function(callback){
35456         this.collapse(false, false);
35457         while(this.firstChild){
35458             this.removeChild(this.firstChild);
35459         }
35460         this.childrenRendered = false;
35461         this.loaded = false;
35462         if(this.isHiddenRoot()){
35463             this.expanded = false;
35464         }
35465         this.expand(false, false, callback);
35466     }
35467 });/*
35468  * Based on:
35469  * Ext JS Library 1.1.1
35470  * Copyright(c) 2006-2007, Ext JS, LLC.
35471  *
35472  * Originally Released Under LGPL - original licence link has changed is not relivant.
35473  *
35474  * Fork - LGPL
35475  * <script type="text/javascript">
35476  */
35477  
35478 /**
35479  * @class Roo.tree.TreeNodeUI
35480  * @constructor
35481  * @param {Object} node The node to render
35482  * The TreeNode UI implementation is separate from the
35483  * tree implementation. Unless you are customizing the tree UI,
35484  * you should never have to use this directly.
35485  */
35486 Roo.tree.TreeNodeUI = function(node){
35487     this.node = node;
35488     this.rendered = false;
35489     this.animating = false;
35490     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35491 };
35492
35493 Roo.tree.TreeNodeUI.prototype = {
35494     removeChild : function(node){
35495         if(this.rendered){
35496             this.ctNode.removeChild(node.ui.getEl());
35497         }
35498     },
35499
35500     beforeLoad : function(){
35501          this.addClass("x-tree-node-loading");
35502     },
35503
35504     afterLoad : function(){
35505          this.removeClass("x-tree-node-loading");
35506     },
35507
35508     onTextChange : function(node, text, oldText){
35509         if(this.rendered){
35510             this.textNode.innerHTML = text;
35511         }
35512     },
35513
35514     onDisableChange : function(node, state){
35515         this.disabled = state;
35516         if(state){
35517             this.addClass("x-tree-node-disabled");
35518         }else{
35519             this.removeClass("x-tree-node-disabled");
35520         }
35521     },
35522
35523     onSelectedChange : function(state){
35524         if(state){
35525             this.focus();
35526             this.addClass("x-tree-selected");
35527         }else{
35528             //this.blur();
35529             this.removeClass("x-tree-selected");
35530         }
35531     },
35532
35533     onMove : function(tree, node, oldParent, newParent, index, refNode){
35534         this.childIndent = null;
35535         if(this.rendered){
35536             var targetNode = newParent.ui.getContainer();
35537             if(!targetNode){//target not rendered
35538                 this.holder = document.createElement("div");
35539                 this.holder.appendChild(this.wrap);
35540                 return;
35541             }
35542             var insertBefore = refNode ? refNode.ui.getEl() : null;
35543             if(insertBefore){
35544                 targetNode.insertBefore(this.wrap, insertBefore);
35545             }else{
35546                 targetNode.appendChild(this.wrap);
35547             }
35548             this.node.renderIndent(true);
35549         }
35550     },
35551
35552     addClass : function(cls){
35553         if(this.elNode){
35554             Roo.fly(this.elNode).addClass(cls);
35555         }
35556     },
35557
35558     removeClass : function(cls){
35559         if(this.elNode){
35560             Roo.fly(this.elNode).removeClass(cls);
35561         }
35562     },
35563
35564     remove : function(){
35565         if(this.rendered){
35566             this.holder = document.createElement("div");
35567             this.holder.appendChild(this.wrap);
35568         }
35569     },
35570
35571     fireEvent : function(){
35572         return this.node.fireEvent.apply(this.node, arguments);
35573     },
35574
35575     initEvents : function(){
35576         this.node.on("move", this.onMove, this);
35577         var E = Roo.EventManager;
35578         var a = this.anchor;
35579
35580         var el = Roo.fly(a, '_treeui');
35581
35582         if(Roo.isOpera){ // opera render bug ignores the CSS
35583             el.setStyle("text-decoration", "none");
35584         }
35585
35586         el.on("click", this.onClick, this);
35587         el.on("dblclick", this.onDblClick, this);
35588
35589         if(this.checkbox){
35590             Roo.EventManager.on(this.checkbox,
35591                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35592         }
35593
35594         el.on("contextmenu", this.onContextMenu, this);
35595
35596         var icon = Roo.fly(this.iconNode);
35597         icon.on("click", this.onClick, this);
35598         icon.on("dblclick", this.onDblClick, this);
35599         icon.on("contextmenu", this.onContextMenu, this);
35600         E.on(this.ecNode, "click", this.ecClick, this, true);
35601
35602         if(this.node.disabled){
35603             this.addClass("x-tree-node-disabled");
35604         }
35605         if(this.node.hidden){
35606             this.addClass("x-tree-node-disabled");
35607         }
35608         var ot = this.node.getOwnerTree();
35609         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35610         if(dd && (!this.node.isRoot || ot.rootVisible)){
35611             Roo.dd.Registry.register(this.elNode, {
35612                 node: this.node,
35613                 handles: this.getDDHandles(),
35614                 isHandle: false
35615             });
35616         }
35617     },
35618
35619     getDDHandles : function(){
35620         return [this.iconNode, this.textNode];
35621     },
35622
35623     hide : function(){
35624         if(this.rendered){
35625             this.wrap.style.display = "none";
35626         }
35627     },
35628
35629     show : function(){
35630         if(this.rendered){
35631             this.wrap.style.display = "";
35632         }
35633     },
35634
35635     onContextMenu : function(e){
35636         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35637             e.preventDefault();
35638             this.focus();
35639             this.fireEvent("contextmenu", this.node, e);
35640         }
35641     },
35642
35643     onClick : function(e){
35644         if(this.dropping){
35645             e.stopEvent();
35646             return;
35647         }
35648         if(this.fireEvent("beforeclick", this.node, e) !== false){
35649             if(!this.disabled && this.node.attributes.href){
35650                 this.fireEvent("click", this.node, e);
35651                 return;
35652             }
35653             e.preventDefault();
35654             if(this.disabled){
35655                 return;
35656             }
35657
35658             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35659                 this.node.toggle();
35660             }
35661
35662             this.fireEvent("click", this.node, e);
35663         }else{
35664             e.stopEvent();
35665         }
35666     },
35667
35668     onDblClick : function(e){
35669         e.preventDefault();
35670         if(this.disabled){
35671             return;
35672         }
35673         if(this.checkbox){
35674             this.toggleCheck();
35675         }
35676         if(!this.animating && this.node.hasChildNodes()){
35677             this.node.toggle();
35678         }
35679         this.fireEvent("dblclick", this.node, e);
35680     },
35681
35682     onCheckChange : function(){
35683         var checked = this.checkbox.checked;
35684         this.node.attributes.checked = checked;
35685         this.fireEvent('checkchange', this.node, checked);
35686     },
35687
35688     ecClick : function(e){
35689         if(!this.animating && this.node.hasChildNodes()){
35690             this.node.toggle();
35691         }
35692     },
35693
35694     startDrop : function(){
35695         this.dropping = true;
35696     },
35697
35698     // delayed drop so the click event doesn't get fired on a drop
35699     endDrop : function(){
35700        setTimeout(function(){
35701            this.dropping = false;
35702        }.createDelegate(this), 50);
35703     },
35704
35705     expand : function(){
35706         this.updateExpandIcon();
35707         this.ctNode.style.display = "";
35708     },
35709
35710     focus : function(){
35711         if(!this.node.preventHScroll){
35712             try{this.anchor.focus();
35713             }catch(e){}
35714         }else if(!Roo.isIE){
35715             try{
35716                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35717                 var l = noscroll.scrollLeft;
35718                 this.anchor.focus();
35719                 noscroll.scrollLeft = l;
35720             }catch(e){}
35721         }
35722     },
35723
35724     toggleCheck : function(value){
35725         var cb = this.checkbox;
35726         if(cb){
35727             cb.checked = (value === undefined ? !cb.checked : value);
35728         }
35729     },
35730
35731     blur : function(){
35732         try{
35733             this.anchor.blur();
35734         }catch(e){}
35735     },
35736
35737     animExpand : function(callback){
35738         var ct = Roo.get(this.ctNode);
35739         ct.stopFx();
35740         if(!this.node.hasChildNodes()){
35741             this.updateExpandIcon();
35742             this.ctNode.style.display = "";
35743             Roo.callback(callback);
35744             return;
35745         }
35746         this.animating = true;
35747         this.updateExpandIcon();
35748
35749         ct.slideIn('t', {
35750            callback : function(){
35751                this.animating = false;
35752                Roo.callback(callback);
35753             },
35754             scope: this,
35755             duration: this.node.ownerTree.duration || .25
35756         });
35757     },
35758
35759     highlight : function(){
35760         var tree = this.node.getOwnerTree();
35761         Roo.fly(this.wrap).highlight(
35762             tree.hlColor || "C3DAF9",
35763             {endColor: tree.hlBaseColor}
35764         );
35765     },
35766
35767     collapse : function(){
35768         this.updateExpandIcon();
35769         this.ctNode.style.display = "none";
35770     },
35771
35772     animCollapse : function(callback){
35773         var ct = Roo.get(this.ctNode);
35774         ct.enableDisplayMode('block');
35775         ct.stopFx();
35776
35777         this.animating = true;
35778         this.updateExpandIcon();
35779
35780         ct.slideOut('t', {
35781             callback : function(){
35782                this.animating = false;
35783                Roo.callback(callback);
35784             },
35785             scope: this,
35786             duration: this.node.ownerTree.duration || .25
35787         });
35788     },
35789
35790     getContainer : function(){
35791         return this.ctNode;
35792     },
35793
35794     getEl : function(){
35795         return this.wrap;
35796     },
35797
35798     appendDDGhost : function(ghostNode){
35799         ghostNode.appendChild(this.elNode.cloneNode(true));
35800     },
35801
35802     getDDRepairXY : function(){
35803         return Roo.lib.Dom.getXY(this.iconNode);
35804     },
35805
35806     onRender : function(){
35807         this.render();
35808     },
35809
35810     render : function(bulkRender){
35811         var n = this.node, a = n.attributes;
35812         var targetNode = n.parentNode ?
35813               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35814
35815         if(!this.rendered){
35816             this.rendered = true;
35817
35818             this.renderElements(n, a, targetNode, bulkRender);
35819
35820             if(a.qtip){
35821                if(this.textNode.setAttributeNS){
35822                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35823                    if(a.qtipTitle){
35824                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35825                    }
35826                }else{
35827                    this.textNode.setAttribute("ext:qtip", a.qtip);
35828                    if(a.qtipTitle){
35829                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35830                    }
35831                }
35832             }else if(a.qtipCfg){
35833                 a.qtipCfg.target = Roo.id(this.textNode);
35834                 Roo.QuickTips.register(a.qtipCfg);
35835             }
35836             this.initEvents();
35837             if(!this.node.expanded){
35838                 this.updateExpandIcon();
35839             }
35840         }else{
35841             if(bulkRender === true) {
35842                 targetNode.appendChild(this.wrap);
35843             }
35844         }
35845     },
35846
35847     renderElements : function(n, a, targetNode, bulkRender)
35848     {
35849         // add some indent caching, this helps performance when rendering a large tree
35850         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35851         var t = n.getOwnerTree();
35852         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35853         if (typeof(n.attributes.html) != 'undefined') {
35854             txt = n.attributes.html;
35855         }
35856         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35857         var cb = typeof a.checked == 'boolean';
35858         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35859         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35860             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35861             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35862             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35863             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35864             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35865              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35866                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35867             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35868             "</li>"];
35869
35870         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35871             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35872                                 n.nextSibling.ui.getEl(), buf.join(""));
35873         }else{
35874             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35875         }
35876
35877         this.elNode = this.wrap.childNodes[0];
35878         this.ctNode = this.wrap.childNodes[1];
35879         var cs = this.elNode.childNodes;
35880         this.indentNode = cs[0];
35881         this.ecNode = cs[1];
35882         this.iconNode = cs[2];
35883         var index = 3;
35884         if(cb){
35885             this.checkbox = cs[3];
35886             index++;
35887         }
35888         this.anchor = cs[index];
35889         this.textNode = cs[index].firstChild;
35890     },
35891
35892     getAnchor : function(){
35893         return this.anchor;
35894     },
35895
35896     getTextEl : function(){
35897         return this.textNode;
35898     },
35899
35900     getIconEl : function(){
35901         return this.iconNode;
35902     },
35903
35904     isChecked : function(){
35905         return this.checkbox ? this.checkbox.checked : false;
35906     },
35907
35908     updateExpandIcon : function(){
35909         if(this.rendered){
35910             var n = this.node, c1, c2;
35911             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35912             var hasChild = n.hasChildNodes();
35913             if(hasChild){
35914                 if(n.expanded){
35915                     cls += "-minus";
35916                     c1 = "x-tree-node-collapsed";
35917                     c2 = "x-tree-node-expanded";
35918                 }else{
35919                     cls += "-plus";
35920                     c1 = "x-tree-node-expanded";
35921                     c2 = "x-tree-node-collapsed";
35922                 }
35923                 if(this.wasLeaf){
35924                     this.removeClass("x-tree-node-leaf");
35925                     this.wasLeaf = false;
35926                 }
35927                 if(this.c1 != c1 || this.c2 != c2){
35928                     Roo.fly(this.elNode).replaceClass(c1, c2);
35929                     this.c1 = c1; this.c2 = c2;
35930                 }
35931             }else{
35932                 // this changes non-leafs into leafs if they have no children.
35933                 // it's not very rational behaviour..
35934                 
35935                 if(!this.wasLeaf && this.node.leaf){
35936                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35937                     delete this.c1;
35938                     delete this.c2;
35939                     this.wasLeaf = true;
35940                 }
35941             }
35942             var ecc = "x-tree-ec-icon "+cls;
35943             if(this.ecc != ecc){
35944                 this.ecNode.className = ecc;
35945                 this.ecc = ecc;
35946             }
35947         }
35948     },
35949
35950     getChildIndent : function(){
35951         if(!this.childIndent){
35952             var buf = [];
35953             var p = this.node;
35954             while(p){
35955                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35956                     if(!p.isLast()) {
35957                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35958                     } else {
35959                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35960                     }
35961                 }
35962                 p = p.parentNode;
35963             }
35964             this.childIndent = buf.join("");
35965         }
35966         return this.childIndent;
35967     },
35968
35969     renderIndent : function(){
35970         if(this.rendered){
35971             var indent = "";
35972             var p = this.node.parentNode;
35973             if(p){
35974                 indent = p.ui.getChildIndent();
35975             }
35976             if(this.indentMarkup != indent){ // don't rerender if not required
35977                 this.indentNode.innerHTML = indent;
35978                 this.indentMarkup = indent;
35979             }
35980             this.updateExpandIcon();
35981         }
35982     }
35983 };
35984
35985 Roo.tree.RootTreeNodeUI = function(){
35986     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35987 };
35988 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35989     render : function(){
35990         if(!this.rendered){
35991             var targetNode = this.node.ownerTree.innerCt.dom;
35992             this.node.expanded = true;
35993             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35994             this.wrap = this.ctNode = targetNode.firstChild;
35995         }
35996     },
35997     collapse : function(){
35998     },
35999     expand : function(){
36000     }
36001 });/*
36002  * Based on:
36003  * Ext JS Library 1.1.1
36004  * Copyright(c) 2006-2007, Ext JS, LLC.
36005  *
36006  * Originally Released Under LGPL - original licence link has changed is not relivant.
36007  *
36008  * Fork - LGPL
36009  * <script type="text/javascript">
36010  */
36011 /**
36012  * @class Roo.tree.TreeLoader
36013  * @extends Roo.util.Observable
36014  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36015  * nodes from a specified URL. The response must be a javascript Array definition
36016  * who's elements are node definition objects. eg:
36017  * <pre><code>
36018 {  success : true,
36019    data :      [
36020    
36021     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36022     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36023     ]
36024 }
36025
36026
36027 </code></pre>
36028  * <br><br>
36029  * The old style respose with just an array is still supported, but not recommended.
36030  * <br><br>
36031  *
36032  * A server request is sent, and child nodes are loaded only when a node is expanded.
36033  * The loading node's id is passed to the server under the parameter name "node" to
36034  * enable the server to produce the correct child nodes.
36035  * <br><br>
36036  * To pass extra parameters, an event handler may be attached to the "beforeload"
36037  * event, and the parameters specified in the TreeLoader's baseParams property:
36038  * <pre><code>
36039     myTreeLoader.on("beforeload", function(treeLoader, node) {
36040         this.baseParams.category = node.attributes.category;
36041     }, this);
36042     
36043 </code></pre>
36044  *
36045  * This would pass an HTTP parameter called "category" to the server containing
36046  * the value of the Node's "category" attribute.
36047  * @constructor
36048  * Creates a new Treeloader.
36049  * @param {Object} config A config object containing config properties.
36050  */
36051 Roo.tree.TreeLoader = function(config){
36052     this.baseParams = {};
36053     this.requestMethod = "POST";
36054     Roo.apply(this, config);
36055
36056     this.addEvents({
36057     
36058         /**
36059          * @event beforeload
36060          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36061          * @param {Object} This TreeLoader object.
36062          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36063          * @param {Object} callback The callback function specified in the {@link #load} call.
36064          */
36065         beforeload : true,
36066         /**
36067          * @event load
36068          * Fires when the node has been successfuly loaded.
36069          * @param {Object} This TreeLoader object.
36070          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36071          * @param {Object} response The response object containing the data from the server.
36072          */
36073         load : true,
36074         /**
36075          * @event loadexception
36076          * Fires if the network request failed.
36077          * @param {Object} This TreeLoader object.
36078          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36079          * @param {Object} response The response object containing the data from the server.
36080          */
36081         loadexception : true,
36082         /**
36083          * @event create
36084          * Fires before a node is created, enabling you to return custom Node types 
36085          * @param {Object} This TreeLoader object.
36086          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36087          */
36088         create : true
36089     });
36090
36091     Roo.tree.TreeLoader.superclass.constructor.call(this);
36092 };
36093
36094 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36095     /**
36096     * @cfg {String} dataUrl The URL from which to request a Json string which
36097     * specifies an array of node definition object representing the child nodes
36098     * to be loaded.
36099     */
36100     /**
36101     * @cfg {String} requestMethod either GET or POST
36102     * defaults to POST (due to BC)
36103     * to be loaded.
36104     */
36105     /**
36106     * @cfg {Object} baseParams (optional) An object containing properties which
36107     * specify HTTP parameters to be passed to each request for child nodes.
36108     */
36109     /**
36110     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36111     * created by this loader. If the attributes sent by the server have an attribute in this object,
36112     * they take priority.
36113     */
36114     /**
36115     * @cfg {Object} uiProviders (optional) An object containing properties which
36116     * 
36117     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36118     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36119     * <i>uiProvider</i> attribute of a returned child node is a string rather
36120     * than a reference to a TreeNodeUI implementation, this that string value
36121     * is used as a property name in the uiProviders object. You can define the provider named
36122     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36123     */
36124     uiProviders : {},
36125
36126     /**
36127     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36128     * child nodes before loading.
36129     */
36130     clearOnLoad : true,
36131
36132     /**
36133     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36134     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36135     * Grid query { data : [ .....] }
36136     */
36137     
36138     root : false,
36139      /**
36140     * @cfg {String} queryParam (optional) 
36141     * Name of the query as it will be passed on the querystring (defaults to 'node')
36142     * eg. the request will be ?node=[id]
36143     */
36144     
36145     
36146     queryParam: false,
36147     
36148     /**
36149      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36150      * This is called automatically when a node is expanded, but may be used to reload
36151      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36152      * @param {Roo.tree.TreeNode} node
36153      * @param {Function} callback
36154      */
36155     load : function(node, callback){
36156         if(this.clearOnLoad){
36157             while(node.firstChild){
36158                 node.removeChild(node.firstChild);
36159             }
36160         }
36161         if(node.attributes.children){ // preloaded json children
36162             var cs = node.attributes.children;
36163             for(var i = 0, len = cs.length; i < len; i++){
36164                 node.appendChild(this.createNode(cs[i]));
36165             }
36166             if(typeof callback == "function"){
36167                 callback();
36168             }
36169         }else if(this.dataUrl){
36170             this.requestData(node, callback);
36171         }
36172     },
36173
36174     getParams: function(node){
36175         var buf = [], bp = this.baseParams;
36176         for(var key in bp){
36177             if(typeof bp[key] != "function"){
36178                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36179             }
36180         }
36181         var n = this.queryParam === false ? 'node' : this.queryParam;
36182         buf.push(n + "=", encodeURIComponent(node.id));
36183         return buf.join("");
36184     },
36185
36186     requestData : function(node, callback){
36187         if(this.fireEvent("beforeload", this, node, callback) !== false){
36188             this.transId = Roo.Ajax.request({
36189                 method:this.requestMethod,
36190                 url: this.dataUrl||this.url,
36191                 success: this.handleResponse,
36192                 failure: this.handleFailure,
36193                 scope: this,
36194                 argument: {callback: callback, node: node},
36195                 params: this.getParams(node)
36196             });
36197         }else{
36198             // if the load is cancelled, make sure we notify
36199             // the node that we are done
36200             if(typeof callback == "function"){
36201                 callback();
36202             }
36203         }
36204     },
36205
36206     isLoading : function(){
36207         return this.transId ? true : false;
36208     },
36209
36210     abort : function(){
36211         if(this.isLoading()){
36212             Roo.Ajax.abort(this.transId);
36213         }
36214     },
36215
36216     // private
36217     createNode : function(attr)
36218     {
36219         // apply baseAttrs, nice idea Corey!
36220         if(this.baseAttrs){
36221             Roo.applyIf(attr, this.baseAttrs);
36222         }
36223         if(this.applyLoader !== false){
36224             attr.loader = this;
36225         }
36226         // uiProvider = depreciated..
36227         
36228         if(typeof(attr.uiProvider) == 'string'){
36229            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36230                 /**  eval:var:attr */ eval(attr.uiProvider);
36231         }
36232         if(typeof(this.uiProviders['default']) != 'undefined') {
36233             attr.uiProvider = this.uiProviders['default'];
36234         }
36235         
36236         this.fireEvent('create', this, attr);
36237         
36238         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36239         return(attr.leaf ?
36240                         new Roo.tree.TreeNode(attr) :
36241                         new Roo.tree.AsyncTreeNode(attr));
36242     },
36243
36244     processResponse : function(response, node, callback)
36245     {
36246         var json = response.responseText;
36247         try {
36248             
36249             var o = Roo.decode(json);
36250             
36251             if (this.root === false && typeof(o.success) != undefined) {
36252                 this.root = 'data'; // the default behaviour for list like data..
36253                 }
36254                 
36255             if (this.root !== false &&  !o.success) {
36256                 // it's a failure condition.
36257                 var a = response.argument;
36258                 this.fireEvent("loadexception", this, a.node, response);
36259                 Roo.log("Load failed - should have a handler really");
36260                 return;
36261             }
36262             
36263             
36264             
36265             if (this.root !== false) {
36266                  o = o[this.root];
36267             }
36268             
36269             for(var i = 0, len = o.length; i < len; i++){
36270                 var n = this.createNode(o[i]);
36271                 if(n){
36272                     node.appendChild(n);
36273                 }
36274             }
36275             if(typeof callback == "function"){
36276                 callback(this, node);
36277             }
36278         }catch(e){
36279             this.handleFailure(response);
36280         }
36281     },
36282
36283     handleResponse : function(response){
36284         this.transId = false;
36285         var a = response.argument;
36286         this.processResponse(response, a.node, a.callback);
36287         this.fireEvent("load", this, a.node, response);
36288     },
36289
36290     handleFailure : function(response)
36291     {
36292         // should handle failure better..
36293         this.transId = false;
36294         var a = response.argument;
36295         this.fireEvent("loadexception", this, a.node, response);
36296         if(typeof a.callback == "function"){
36297             a.callback(this, a.node);
36298         }
36299     }
36300 });/*
36301  * Based on:
36302  * Ext JS Library 1.1.1
36303  * Copyright(c) 2006-2007, Ext JS, LLC.
36304  *
36305  * Originally Released Under LGPL - original licence link has changed is not relivant.
36306  *
36307  * Fork - LGPL
36308  * <script type="text/javascript">
36309  */
36310
36311 /**
36312 * @class Roo.tree.TreeFilter
36313 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36314 * @param {TreePanel} tree
36315 * @param {Object} config (optional)
36316  */
36317 Roo.tree.TreeFilter = function(tree, config){
36318     this.tree = tree;
36319     this.filtered = {};
36320     Roo.apply(this, config);
36321 };
36322
36323 Roo.tree.TreeFilter.prototype = {
36324     clearBlank:false,
36325     reverse:false,
36326     autoClear:false,
36327     remove:false,
36328
36329      /**
36330      * Filter the data by a specific attribute.
36331      * @param {String/RegExp} value Either string that the attribute value
36332      * should start with or a RegExp to test against the attribute
36333      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36334      * @param {TreeNode} startNode (optional) The node to start the filter at.
36335      */
36336     filter : function(value, attr, startNode){
36337         attr = attr || "text";
36338         var f;
36339         if(typeof value == "string"){
36340             var vlen = value.length;
36341             // auto clear empty filter
36342             if(vlen == 0 && this.clearBlank){
36343                 this.clear();
36344                 return;
36345             }
36346             value = value.toLowerCase();
36347             f = function(n){
36348                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36349             };
36350         }else if(value.exec){ // regex?
36351             f = function(n){
36352                 return value.test(n.attributes[attr]);
36353             };
36354         }else{
36355             throw 'Illegal filter type, must be string or regex';
36356         }
36357         this.filterBy(f, null, startNode);
36358         },
36359
36360     /**
36361      * Filter by a function. The passed function will be called with each
36362      * node in the tree (or from the startNode). If the function returns true, the node is kept
36363      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36364      * @param {Function} fn The filter function
36365      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36366      */
36367     filterBy : function(fn, scope, startNode){
36368         startNode = startNode || this.tree.root;
36369         if(this.autoClear){
36370             this.clear();
36371         }
36372         var af = this.filtered, rv = this.reverse;
36373         var f = function(n){
36374             if(n == startNode){
36375                 return true;
36376             }
36377             if(af[n.id]){
36378                 return false;
36379             }
36380             var m = fn.call(scope || n, n);
36381             if(!m || rv){
36382                 af[n.id] = n;
36383                 n.ui.hide();
36384                 return false;
36385             }
36386             return true;
36387         };
36388         startNode.cascade(f);
36389         if(this.remove){
36390            for(var id in af){
36391                if(typeof id != "function"){
36392                    var n = af[id];
36393                    if(n && n.parentNode){
36394                        n.parentNode.removeChild(n);
36395                    }
36396                }
36397            }
36398         }
36399     },
36400
36401     /**
36402      * Clears the current filter. Note: with the "remove" option
36403      * set a filter cannot be cleared.
36404      */
36405     clear : function(){
36406         var t = this.tree;
36407         var af = this.filtered;
36408         for(var id in af){
36409             if(typeof id != "function"){
36410                 var n = af[id];
36411                 if(n){
36412                     n.ui.show();
36413                 }
36414             }
36415         }
36416         this.filtered = {};
36417     }
36418 };
36419 /*
36420  * Based on:
36421  * Ext JS Library 1.1.1
36422  * Copyright(c) 2006-2007, Ext JS, LLC.
36423  *
36424  * Originally Released Under LGPL - original licence link has changed is not relivant.
36425  *
36426  * Fork - LGPL
36427  * <script type="text/javascript">
36428  */
36429  
36430
36431 /**
36432  * @class Roo.tree.TreeSorter
36433  * Provides sorting of nodes in a TreePanel
36434  * 
36435  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36436  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36437  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36438  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36439  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36440  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36441  * @constructor
36442  * @param {TreePanel} tree
36443  * @param {Object} config
36444  */
36445 Roo.tree.TreeSorter = function(tree, config){
36446     Roo.apply(this, config);
36447     tree.on("beforechildrenrendered", this.doSort, this);
36448     tree.on("append", this.updateSort, this);
36449     tree.on("insert", this.updateSort, this);
36450     
36451     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36452     var p = this.property || "text";
36453     var sortType = this.sortType;
36454     var fs = this.folderSort;
36455     var cs = this.caseSensitive === true;
36456     var leafAttr = this.leafAttr || 'leaf';
36457
36458     this.sortFn = function(n1, n2){
36459         if(fs){
36460             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36461                 return 1;
36462             }
36463             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36464                 return -1;
36465             }
36466         }
36467         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36468         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36469         if(v1 < v2){
36470                         return dsc ? +1 : -1;
36471                 }else if(v1 > v2){
36472                         return dsc ? -1 : +1;
36473         }else{
36474                 return 0;
36475         }
36476     };
36477 };
36478
36479 Roo.tree.TreeSorter.prototype = {
36480     doSort : function(node){
36481         node.sort(this.sortFn);
36482     },
36483     
36484     compareNodes : function(n1, n2){
36485         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36486     },
36487     
36488     updateSort : function(tree, node){
36489         if(node.childrenRendered){
36490             this.doSort.defer(1, this, [node]);
36491         }
36492     }
36493 };/*
36494  * Based on:
36495  * Ext JS Library 1.1.1
36496  * Copyright(c) 2006-2007, Ext JS, LLC.
36497  *
36498  * Originally Released Under LGPL - original licence link has changed is not relivant.
36499  *
36500  * Fork - LGPL
36501  * <script type="text/javascript">
36502  */
36503
36504 if(Roo.dd.DropZone){
36505     
36506 Roo.tree.TreeDropZone = function(tree, config){
36507     this.allowParentInsert = false;
36508     this.allowContainerDrop = false;
36509     this.appendOnly = false;
36510     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36511     this.tree = tree;
36512     this.lastInsertClass = "x-tree-no-status";
36513     this.dragOverData = {};
36514 };
36515
36516 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36517     ddGroup : "TreeDD",
36518     scroll:  true,
36519     
36520     expandDelay : 1000,
36521     
36522     expandNode : function(node){
36523         if(node.hasChildNodes() && !node.isExpanded()){
36524             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36525         }
36526     },
36527     
36528     queueExpand : function(node){
36529         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36530     },
36531     
36532     cancelExpand : function(){
36533         if(this.expandProcId){
36534             clearTimeout(this.expandProcId);
36535             this.expandProcId = false;
36536         }
36537     },
36538     
36539     isValidDropPoint : function(n, pt, dd, e, data){
36540         if(!n || !data){ return false; }
36541         var targetNode = n.node;
36542         var dropNode = data.node;
36543         // default drop rules
36544         if(!(targetNode && targetNode.isTarget && pt)){
36545             return false;
36546         }
36547         if(pt == "append" && targetNode.allowChildren === false){
36548             return false;
36549         }
36550         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36551             return false;
36552         }
36553         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36554             return false;
36555         }
36556         // reuse the object
36557         var overEvent = this.dragOverData;
36558         overEvent.tree = this.tree;
36559         overEvent.target = targetNode;
36560         overEvent.data = data;
36561         overEvent.point = pt;
36562         overEvent.source = dd;
36563         overEvent.rawEvent = e;
36564         overEvent.dropNode = dropNode;
36565         overEvent.cancel = false;  
36566         var result = this.tree.fireEvent("nodedragover", overEvent);
36567         return overEvent.cancel === false && result !== false;
36568     },
36569     
36570     getDropPoint : function(e, n, dd)
36571     {
36572         var tn = n.node;
36573         if(tn.isRoot){
36574             return tn.allowChildren !== false ? "append" : false; // always append for root
36575         }
36576         var dragEl = n.ddel;
36577         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36578         var y = Roo.lib.Event.getPageY(e);
36579         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36580         
36581         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36582         var noAppend = tn.allowChildren === false;
36583         if(this.appendOnly || tn.parentNode.allowChildren === false){
36584             return noAppend ? false : "append";
36585         }
36586         var noBelow = false;
36587         if(!this.allowParentInsert){
36588             noBelow = tn.hasChildNodes() && tn.isExpanded();
36589         }
36590         var q = (b - t) / (noAppend ? 2 : 3);
36591         if(y >= t && y < (t + q)){
36592             return "above";
36593         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36594             return "below";
36595         }else{
36596             return "append";
36597         }
36598     },
36599     
36600     onNodeEnter : function(n, dd, e, data)
36601     {
36602         this.cancelExpand();
36603     },
36604     
36605     onNodeOver : function(n, dd, e, data)
36606     {
36607        
36608         var pt = this.getDropPoint(e, n, dd);
36609         var node = n.node;
36610         
36611         // auto node expand check
36612         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36613             this.queueExpand(node);
36614         }else if(pt != "append"){
36615             this.cancelExpand();
36616         }
36617         
36618         // set the insert point style on the target node
36619         var returnCls = this.dropNotAllowed;
36620         if(this.isValidDropPoint(n, pt, dd, e, data)){
36621            if(pt){
36622                var el = n.ddel;
36623                var cls;
36624                if(pt == "above"){
36625                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36626                    cls = "x-tree-drag-insert-above";
36627                }else if(pt == "below"){
36628                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36629                    cls = "x-tree-drag-insert-below";
36630                }else{
36631                    returnCls = "x-tree-drop-ok-append";
36632                    cls = "x-tree-drag-append";
36633                }
36634                if(this.lastInsertClass != cls){
36635                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36636                    this.lastInsertClass = cls;
36637                }
36638            }
36639        }
36640        return returnCls;
36641     },
36642     
36643     onNodeOut : function(n, dd, e, data){
36644         
36645         this.cancelExpand();
36646         this.removeDropIndicators(n);
36647     },
36648     
36649     onNodeDrop : function(n, dd, e, data){
36650         var point = this.getDropPoint(e, n, dd);
36651         var targetNode = n.node;
36652         targetNode.ui.startDrop();
36653         if(!this.isValidDropPoint(n, point, dd, e, data)){
36654             targetNode.ui.endDrop();
36655             return false;
36656         }
36657         // first try to find the drop node
36658         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36659         var dropEvent = {
36660             tree : this.tree,
36661             target: targetNode,
36662             data: data,
36663             point: point,
36664             source: dd,
36665             rawEvent: e,
36666             dropNode: dropNode,
36667             cancel: !dropNode   
36668         };
36669         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36670         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36671             targetNode.ui.endDrop();
36672             return false;
36673         }
36674         // allow target changing
36675         targetNode = dropEvent.target;
36676         if(point == "append" && !targetNode.isExpanded()){
36677             targetNode.expand(false, null, function(){
36678                 this.completeDrop(dropEvent);
36679             }.createDelegate(this));
36680         }else{
36681             this.completeDrop(dropEvent);
36682         }
36683         return true;
36684     },
36685     
36686     completeDrop : function(de){
36687         var ns = de.dropNode, p = de.point, t = de.target;
36688         if(!(ns instanceof Array)){
36689             ns = [ns];
36690         }
36691         var n;
36692         for(var i = 0, len = ns.length; i < len; i++){
36693             n = ns[i];
36694             if(p == "above"){
36695                 t.parentNode.insertBefore(n, t);
36696             }else if(p == "below"){
36697                 t.parentNode.insertBefore(n, t.nextSibling);
36698             }else{
36699                 t.appendChild(n);
36700             }
36701         }
36702         n.ui.focus();
36703         if(this.tree.hlDrop){
36704             n.ui.highlight();
36705         }
36706         t.ui.endDrop();
36707         this.tree.fireEvent("nodedrop", de);
36708     },
36709     
36710     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36711         if(this.tree.hlDrop){
36712             dropNode.ui.focus();
36713             dropNode.ui.highlight();
36714         }
36715         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36716     },
36717     
36718     getTree : function(){
36719         return this.tree;
36720     },
36721     
36722     removeDropIndicators : function(n){
36723         if(n && n.ddel){
36724             var el = n.ddel;
36725             Roo.fly(el).removeClass([
36726                     "x-tree-drag-insert-above",
36727                     "x-tree-drag-insert-below",
36728                     "x-tree-drag-append"]);
36729             this.lastInsertClass = "_noclass";
36730         }
36731     },
36732     
36733     beforeDragDrop : function(target, e, id){
36734         this.cancelExpand();
36735         return true;
36736     },
36737     
36738     afterRepair : function(data){
36739         if(data && Roo.enableFx){
36740             data.node.ui.highlight();
36741         }
36742         this.hideProxy();
36743     } 
36744     
36745 });
36746
36747 }
36748 /*
36749  * Based on:
36750  * Ext JS Library 1.1.1
36751  * Copyright(c) 2006-2007, Ext JS, LLC.
36752  *
36753  * Originally Released Under LGPL - original licence link has changed is not relivant.
36754  *
36755  * Fork - LGPL
36756  * <script type="text/javascript">
36757  */
36758  
36759
36760 if(Roo.dd.DragZone){
36761 Roo.tree.TreeDragZone = function(tree, config){
36762     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36763     this.tree = tree;
36764 };
36765
36766 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36767     ddGroup : "TreeDD",
36768    
36769     onBeforeDrag : function(data, e){
36770         var n = data.node;
36771         return n && n.draggable && !n.disabled;
36772     },
36773      
36774     
36775     onInitDrag : function(e){
36776         var data = this.dragData;
36777         this.tree.getSelectionModel().select(data.node);
36778         this.proxy.update("");
36779         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36780         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36781     },
36782     
36783     getRepairXY : function(e, data){
36784         return data.node.ui.getDDRepairXY();
36785     },
36786     
36787     onEndDrag : function(data, e){
36788         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36789         
36790         
36791     },
36792     
36793     onValidDrop : function(dd, e, id){
36794         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36795         this.hideProxy();
36796     },
36797     
36798     beforeInvalidDrop : function(e, id){
36799         // this scrolls the original position back into view
36800         var sm = this.tree.getSelectionModel();
36801         sm.clearSelections();
36802         sm.select(this.dragData.node);
36803     }
36804 });
36805 }/*
36806  * Based on:
36807  * Ext JS Library 1.1.1
36808  * Copyright(c) 2006-2007, Ext JS, LLC.
36809  *
36810  * Originally Released Under LGPL - original licence link has changed is not relivant.
36811  *
36812  * Fork - LGPL
36813  * <script type="text/javascript">
36814  */
36815 /**
36816  * @class Roo.tree.TreeEditor
36817  * @extends Roo.Editor
36818  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36819  * as the editor field.
36820  * @constructor
36821  * @param {Object} config (used to be the tree panel.)
36822  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36823  * 
36824  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36825  * @cfg {Roo.form.TextField|Object} field The field configuration
36826  *
36827  * 
36828  */
36829 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36830     var tree = config;
36831     var field;
36832     if (oldconfig) { // old style..
36833         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36834     } else {
36835         // new style..
36836         tree = config.tree;
36837         config.field = config.field  || {};
36838         config.field.xtype = 'TextField';
36839         field = Roo.factory(config.field, Roo.form);
36840     }
36841     config = config || {};
36842     
36843     
36844     this.addEvents({
36845         /**
36846          * @event beforenodeedit
36847          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36848          * false from the handler of this event.
36849          * @param {Editor} this
36850          * @param {Roo.tree.Node} node 
36851          */
36852         "beforenodeedit" : true
36853     });
36854     
36855     //Roo.log(config);
36856     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36857
36858     this.tree = tree;
36859
36860     tree.on('beforeclick', this.beforeNodeClick, this);
36861     tree.getTreeEl().on('mousedown', this.hide, this);
36862     this.on('complete', this.updateNode, this);
36863     this.on('beforestartedit', this.fitToTree, this);
36864     this.on('startedit', this.bindScroll, this, {delay:10});
36865     this.on('specialkey', this.onSpecialKey, this);
36866 };
36867
36868 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36869     /**
36870      * @cfg {String} alignment
36871      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36872      */
36873     alignment: "l-l",
36874     // inherit
36875     autoSize: false,
36876     /**
36877      * @cfg {Boolean} hideEl
36878      * True to hide the bound element while the editor is displayed (defaults to false)
36879      */
36880     hideEl : false,
36881     /**
36882      * @cfg {String} cls
36883      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36884      */
36885     cls: "x-small-editor x-tree-editor",
36886     /**
36887      * @cfg {Boolean} shim
36888      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36889      */
36890     shim:false,
36891     // inherit
36892     shadow:"frame",
36893     /**
36894      * @cfg {Number} maxWidth
36895      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36896      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36897      * scroll and client offsets into account prior to each edit.
36898      */
36899     maxWidth: 250,
36900
36901     editDelay : 350,
36902
36903     // private
36904     fitToTree : function(ed, el){
36905         var td = this.tree.getTreeEl().dom, nd = el.dom;
36906         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36907             td.scrollLeft = nd.offsetLeft;
36908         }
36909         var w = Math.min(
36910                 this.maxWidth,
36911                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36912         this.setSize(w, '');
36913         
36914         return this.fireEvent('beforenodeedit', this, this.editNode);
36915         
36916     },
36917
36918     // private
36919     triggerEdit : function(node){
36920         this.completeEdit();
36921         this.editNode = node;
36922         this.startEdit(node.ui.textNode, node.text);
36923     },
36924
36925     // private
36926     bindScroll : function(){
36927         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36928     },
36929
36930     // private
36931     beforeNodeClick : function(node, e){
36932         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36933         this.lastClick = new Date();
36934         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36935             e.stopEvent();
36936             this.triggerEdit(node);
36937             return false;
36938         }
36939         return true;
36940     },
36941
36942     // private
36943     updateNode : function(ed, value){
36944         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36945         this.editNode.setText(value);
36946     },
36947
36948     // private
36949     onHide : function(){
36950         Roo.tree.TreeEditor.superclass.onHide.call(this);
36951         if(this.editNode){
36952             this.editNode.ui.focus();
36953         }
36954     },
36955
36956     // private
36957     onSpecialKey : function(field, e){
36958         var k = e.getKey();
36959         if(k == e.ESC){
36960             e.stopEvent();
36961             this.cancelEdit();
36962         }else if(k == e.ENTER && !e.hasModifier()){
36963             e.stopEvent();
36964             this.completeEdit();
36965         }
36966     }
36967 });//<Script type="text/javascript">
36968 /*
36969  * Based on:
36970  * Ext JS Library 1.1.1
36971  * Copyright(c) 2006-2007, Ext JS, LLC.
36972  *
36973  * Originally Released Under LGPL - original licence link has changed is not relivant.
36974  *
36975  * Fork - LGPL
36976  * <script type="text/javascript">
36977  */
36978  
36979 /**
36980  * Not documented??? - probably should be...
36981  */
36982
36983 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36984     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36985     
36986     renderElements : function(n, a, targetNode, bulkRender){
36987         //consel.log("renderElements?");
36988         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36989
36990         var t = n.getOwnerTree();
36991         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36992         
36993         var cols = t.columns;
36994         var bw = t.borderWidth;
36995         var c = cols[0];
36996         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36997          var cb = typeof a.checked == "boolean";
36998         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36999         var colcls = 'x-t-' + tid + '-c0';
37000         var buf = [
37001             '<li class="x-tree-node">',
37002             
37003                 
37004                 '<div class="x-tree-node-el ', a.cls,'">',
37005                     // extran...
37006                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37007                 
37008                 
37009                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37010                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37011                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37012                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37013                            (a.iconCls ? ' '+a.iconCls : ''),
37014                            '" unselectable="on" />',
37015                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37016                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37017                              
37018                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37019                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37020                             '<span unselectable="on" qtip="' + tx + '">',
37021                              tx,
37022                              '</span></a>' ,
37023                     '</div>',
37024                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37025                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37026                  ];
37027         for(var i = 1, len = cols.length; i < len; i++){
37028             c = cols[i];
37029             colcls = 'x-t-' + tid + '-c' +i;
37030             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37031             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37032                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37033                       "</div>");
37034          }
37035          
37036          buf.push(
37037             '</a>',
37038             '<div class="x-clear"></div></div>',
37039             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37040             "</li>");
37041         
37042         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37043             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37044                                 n.nextSibling.ui.getEl(), buf.join(""));
37045         }else{
37046             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37047         }
37048         var el = this.wrap.firstChild;
37049         this.elRow = el;
37050         this.elNode = el.firstChild;
37051         this.ranchor = el.childNodes[1];
37052         this.ctNode = this.wrap.childNodes[1];
37053         var cs = el.firstChild.childNodes;
37054         this.indentNode = cs[0];
37055         this.ecNode = cs[1];
37056         this.iconNode = cs[2];
37057         var index = 3;
37058         if(cb){
37059             this.checkbox = cs[3];
37060             index++;
37061         }
37062         this.anchor = cs[index];
37063         
37064         this.textNode = cs[index].firstChild;
37065         
37066         //el.on("click", this.onClick, this);
37067         //el.on("dblclick", this.onDblClick, this);
37068         
37069         
37070        // console.log(this);
37071     },
37072     initEvents : function(){
37073         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37074         
37075             
37076         var a = this.ranchor;
37077
37078         var el = Roo.get(a);
37079
37080         if(Roo.isOpera){ // opera render bug ignores the CSS
37081             el.setStyle("text-decoration", "none");
37082         }
37083
37084         el.on("click", this.onClick, this);
37085         el.on("dblclick", this.onDblClick, this);
37086         el.on("contextmenu", this.onContextMenu, this);
37087         
37088     },
37089     
37090     /*onSelectedChange : function(state){
37091         if(state){
37092             this.focus();
37093             this.addClass("x-tree-selected");
37094         }else{
37095             //this.blur();
37096             this.removeClass("x-tree-selected");
37097         }
37098     },*/
37099     addClass : function(cls){
37100         if(this.elRow){
37101             Roo.fly(this.elRow).addClass(cls);
37102         }
37103         
37104     },
37105     
37106     
37107     removeClass : function(cls){
37108         if(this.elRow){
37109             Roo.fly(this.elRow).removeClass(cls);
37110         }
37111     }
37112
37113     
37114     
37115 });//<Script type="text/javascript">
37116
37117 /*
37118  * Based on:
37119  * Ext JS Library 1.1.1
37120  * Copyright(c) 2006-2007, Ext JS, LLC.
37121  *
37122  * Originally Released Under LGPL - original licence link has changed is not relivant.
37123  *
37124  * Fork - LGPL
37125  * <script type="text/javascript">
37126  */
37127  
37128
37129 /**
37130  * @class Roo.tree.ColumnTree
37131  * @extends Roo.data.TreePanel
37132  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37133  * @cfg {int} borderWidth  compined right/left border allowance
37134  * @constructor
37135  * @param {String/HTMLElement/Element} el The container element
37136  * @param {Object} config
37137  */
37138 Roo.tree.ColumnTree =  function(el, config)
37139 {
37140    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37141    this.addEvents({
37142         /**
37143         * @event resize
37144         * Fire this event on a container when it resizes
37145         * @param {int} w Width
37146         * @param {int} h Height
37147         */
37148        "resize" : true
37149     });
37150     this.on('resize', this.onResize, this);
37151 };
37152
37153 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37154     //lines:false,
37155     
37156     
37157     borderWidth: Roo.isBorderBox ? 0 : 2, 
37158     headEls : false,
37159     
37160     render : function(){
37161         // add the header.....
37162        
37163         Roo.tree.ColumnTree.superclass.render.apply(this);
37164         
37165         this.el.addClass('x-column-tree');
37166         
37167         this.headers = this.el.createChild(
37168             {cls:'x-tree-headers'},this.innerCt.dom);
37169    
37170         var cols = this.columns, c;
37171         var totalWidth = 0;
37172         this.headEls = [];
37173         var  len = cols.length;
37174         for(var i = 0; i < len; i++){
37175              c = cols[i];
37176              totalWidth += c.width;
37177             this.headEls.push(this.headers.createChild({
37178                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37179                  cn: {
37180                      cls:'x-tree-hd-text',
37181                      html: c.header
37182                  },
37183                  style:'width:'+(c.width-this.borderWidth)+'px;'
37184              }));
37185         }
37186         this.headers.createChild({cls:'x-clear'});
37187         // prevent floats from wrapping when clipped
37188         this.headers.setWidth(totalWidth);
37189         //this.innerCt.setWidth(totalWidth);
37190         this.innerCt.setStyle({ overflow: 'auto' });
37191         this.onResize(this.width, this.height);
37192              
37193         
37194     },
37195     onResize : function(w,h)
37196     {
37197         this.height = h;
37198         this.width = w;
37199         // resize cols..
37200         this.innerCt.setWidth(this.width);
37201         this.innerCt.setHeight(this.height-20);
37202         
37203         // headers...
37204         var cols = this.columns, c;
37205         var totalWidth = 0;
37206         var expEl = false;
37207         var len = cols.length;
37208         for(var i = 0; i < len; i++){
37209             c = cols[i];
37210             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37211                 // it's the expander..
37212                 expEl  = this.headEls[i];
37213                 continue;
37214             }
37215             totalWidth += c.width;
37216             
37217         }
37218         if (expEl) {
37219             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37220         }
37221         this.headers.setWidth(w-20);
37222
37223         
37224         
37225         
37226     }
37227 });
37228 /*
37229  * Based on:
37230  * Ext JS Library 1.1.1
37231  * Copyright(c) 2006-2007, Ext JS, LLC.
37232  *
37233  * Originally Released Under LGPL - original licence link has changed is not relivant.
37234  *
37235  * Fork - LGPL
37236  * <script type="text/javascript">
37237  */
37238  
37239 /**
37240  * @class Roo.menu.Menu
37241  * @extends Roo.util.Observable
37242  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37243  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37244  * @constructor
37245  * Creates a new Menu
37246  * @param {Object} config Configuration options
37247  */
37248 Roo.menu.Menu = function(config){
37249     
37250     Roo.menu.Menu.superclass.constructor.call(this, config);
37251     
37252     this.id = this.id || Roo.id();
37253     this.addEvents({
37254         /**
37255          * @event beforeshow
37256          * Fires before this menu is displayed
37257          * @param {Roo.menu.Menu} this
37258          */
37259         beforeshow : true,
37260         /**
37261          * @event beforehide
37262          * Fires before this menu is hidden
37263          * @param {Roo.menu.Menu} this
37264          */
37265         beforehide : true,
37266         /**
37267          * @event show
37268          * Fires after this menu is displayed
37269          * @param {Roo.menu.Menu} this
37270          */
37271         show : true,
37272         /**
37273          * @event hide
37274          * Fires after this menu is hidden
37275          * @param {Roo.menu.Menu} this
37276          */
37277         hide : true,
37278         /**
37279          * @event click
37280          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37281          * @param {Roo.menu.Menu} this
37282          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37283          * @param {Roo.EventObject} e
37284          */
37285         click : true,
37286         /**
37287          * @event mouseover
37288          * Fires when the mouse is hovering over this menu
37289          * @param {Roo.menu.Menu} this
37290          * @param {Roo.EventObject} e
37291          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37292          */
37293         mouseover : true,
37294         /**
37295          * @event mouseout
37296          * Fires when the mouse exits this menu
37297          * @param {Roo.menu.Menu} this
37298          * @param {Roo.EventObject} e
37299          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37300          */
37301         mouseout : true,
37302         /**
37303          * @event itemclick
37304          * Fires when a menu item contained in this menu is clicked
37305          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37306          * @param {Roo.EventObject} e
37307          */
37308         itemclick: true
37309     });
37310     if (this.registerMenu) {
37311         Roo.menu.MenuMgr.register(this);
37312     }
37313     
37314     var mis = this.items;
37315     this.items = new Roo.util.MixedCollection();
37316     if(mis){
37317         this.add.apply(this, mis);
37318     }
37319 };
37320
37321 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37322     /**
37323      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37324      */
37325     minWidth : 120,
37326     /**
37327      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37328      * for bottom-right shadow (defaults to "sides")
37329      */
37330     shadow : "sides",
37331     /**
37332      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37333      * this menu (defaults to "tl-tr?")
37334      */
37335     subMenuAlign : "tl-tr?",
37336     /**
37337      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37338      * relative to its element of origin (defaults to "tl-bl?")
37339      */
37340     defaultAlign : "tl-bl?",
37341     /**
37342      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37343      */
37344     allowOtherMenus : false,
37345     /**
37346      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37347      */
37348     registerMenu : true,
37349
37350     hidden:true,
37351
37352     // private
37353     render : function(){
37354         if(this.el){
37355             return;
37356         }
37357         var el = this.el = new Roo.Layer({
37358             cls: "x-menu",
37359             shadow:this.shadow,
37360             constrain: false,
37361             parentEl: this.parentEl || document.body,
37362             zindex:15000
37363         });
37364
37365         this.keyNav = new Roo.menu.MenuNav(this);
37366
37367         if(this.plain){
37368             el.addClass("x-menu-plain");
37369         }
37370         if(this.cls){
37371             el.addClass(this.cls);
37372         }
37373         // generic focus element
37374         this.focusEl = el.createChild({
37375             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37376         });
37377         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37378         //disabling touch- as it's causing issues ..
37379         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37380         ul.on('click'   , this.onClick, this);
37381         
37382         
37383         ul.on("mouseover", this.onMouseOver, this);
37384         ul.on("mouseout", this.onMouseOut, this);
37385         this.items.each(function(item){
37386             if (item.hidden) {
37387                 return;
37388             }
37389             
37390             var li = document.createElement("li");
37391             li.className = "x-menu-list-item";
37392             ul.dom.appendChild(li);
37393             item.render(li, this);
37394         }, this);
37395         this.ul = ul;
37396         this.autoWidth();
37397     },
37398
37399     // private
37400     autoWidth : function(){
37401         var el = this.el, ul = this.ul;
37402         if(!el){
37403             return;
37404         }
37405         var w = this.width;
37406         if(w){
37407             el.setWidth(w);
37408         }else if(Roo.isIE){
37409             el.setWidth(this.minWidth);
37410             var t = el.dom.offsetWidth; // force recalc
37411             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37412         }
37413     },
37414
37415     // private
37416     delayAutoWidth : function(){
37417         if(this.rendered){
37418             if(!this.awTask){
37419                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37420             }
37421             this.awTask.delay(20);
37422         }
37423     },
37424
37425     // private
37426     findTargetItem : function(e){
37427         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37428         if(t && t.menuItemId){
37429             return this.items.get(t.menuItemId);
37430         }
37431     },
37432
37433     // private
37434     onClick : function(e){
37435         Roo.log("menu.onClick");
37436         var t = this.findTargetItem(e);
37437         if(!t){
37438             return;
37439         }
37440         Roo.log(e);
37441         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37442             if(t == this.activeItem && t.shouldDeactivate(e)){
37443                 this.activeItem.deactivate();
37444                 delete this.activeItem;
37445                 return;
37446             }
37447             if(t.canActivate){
37448                 this.setActiveItem(t, true);
37449             }
37450             return;
37451             
37452             
37453         }
37454         
37455         t.onClick(e);
37456         this.fireEvent("click", this, t, e);
37457     },
37458
37459     // private
37460     setActiveItem : function(item, autoExpand){
37461         if(item != this.activeItem){
37462             if(this.activeItem){
37463                 this.activeItem.deactivate();
37464             }
37465             this.activeItem = item;
37466             item.activate(autoExpand);
37467         }else if(autoExpand){
37468             item.expandMenu();
37469         }
37470     },
37471
37472     // private
37473     tryActivate : function(start, step){
37474         var items = this.items;
37475         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37476             var item = items.get(i);
37477             if(!item.disabled && item.canActivate){
37478                 this.setActiveItem(item, false);
37479                 return item;
37480             }
37481         }
37482         return false;
37483     },
37484
37485     // private
37486     onMouseOver : function(e){
37487         var t;
37488         if(t = this.findTargetItem(e)){
37489             if(t.canActivate && !t.disabled){
37490                 this.setActiveItem(t, true);
37491             }
37492         }
37493         this.fireEvent("mouseover", this, e, t);
37494     },
37495
37496     // private
37497     onMouseOut : function(e){
37498         var t;
37499         if(t = this.findTargetItem(e)){
37500             if(t == this.activeItem && t.shouldDeactivate(e)){
37501                 this.activeItem.deactivate();
37502                 delete this.activeItem;
37503             }
37504         }
37505         this.fireEvent("mouseout", this, e, t);
37506     },
37507
37508     /**
37509      * Read-only.  Returns true if the menu is currently displayed, else false.
37510      * @type Boolean
37511      */
37512     isVisible : function(){
37513         return this.el && !this.hidden;
37514     },
37515
37516     /**
37517      * Displays this menu relative to another element
37518      * @param {String/HTMLElement/Roo.Element} element The element to align to
37519      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37520      * the element (defaults to this.defaultAlign)
37521      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37522      */
37523     show : function(el, pos, parentMenu){
37524         this.parentMenu = parentMenu;
37525         if(!this.el){
37526             this.render();
37527         }
37528         this.fireEvent("beforeshow", this);
37529         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37530     },
37531
37532     /**
37533      * Displays this menu at a specific xy position
37534      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37535      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37536      */
37537     showAt : function(xy, parentMenu, /* private: */_e){
37538         this.parentMenu = parentMenu;
37539         if(!this.el){
37540             this.render();
37541         }
37542         if(_e !== false){
37543             this.fireEvent("beforeshow", this);
37544             xy = this.el.adjustForConstraints(xy);
37545         }
37546         this.el.setXY(xy);
37547         this.el.show();
37548         this.hidden = false;
37549         this.focus();
37550         this.fireEvent("show", this);
37551     },
37552
37553     focus : function(){
37554         if(!this.hidden){
37555             this.doFocus.defer(50, this);
37556         }
37557     },
37558
37559     doFocus : function(){
37560         if(!this.hidden){
37561             this.focusEl.focus();
37562         }
37563     },
37564
37565     /**
37566      * Hides this menu and optionally all parent menus
37567      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37568      */
37569     hide : function(deep){
37570         if(this.el && this.isVisible()){
37571             this.fireEvent("beforehide", this);
37572             if(this.activeItem){
37573                 this.activeItem.deactivate();
37574                 this.activeItem = null;
37575             }
37576             this.el.hide();
37577             this.hidden = true;
37578             this.fireEvent("hide", this);
37579         }
37580         if(deep === true && this.parentMenu){
37581             this.parentMenu.hide(true);
37582         }
37583     },
37584
37585     /**
37586      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37587      * Any of the following are valid:
37588      * <ul>
37589      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37590      * <li>An HTMLElement object which will be converted to a menu item</li>
37591      * <li>A menu item config object that will be created as a new menu item</li>
37592      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37593      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37594      * </ul>
37595      * Usage:
37596      * <pre><code>
37597 // Create the menu
37598 var menu = new Roo.menu.Menu();
37599
37600 // Create a menu item to add by reference
37601 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37602
37603 // Add a bunch of items at once using different methods.
37604 // Only the last item added will be returned.
37605 var item = menu.add(
37606     menuItem,                // add existing item by ref
37607     'Dynamic Item',          // new TextItem
37608     '-',                     // new separator
37609     { text: 'Config Item' }  // new item by config
37610 );
37611 </code></pre>
37612      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37613      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37614      */
37615     add : function(){
37616         var a = arguments, l = a.length, item;
37617         for(var i = 0; i < l; i++){
37618             var el = a[i];
37619             if ((typeof(el) == "object") && el.xtype && el.xns) {
37620                 el = Roo.factory(el, Roo.menu);
37621             }
37622             
37623             if(el.render){ // some kind of Item
37624                 item = this.addItem(el);
37625             }else if(typeof el == "string"){ // string
37626                 if(el == "separator" || el == "-"){
37627                     item = this.addSeparator();
37628                 }else{
37629                     item = this.addText(el);
37630                 }
37631             }else if(el.tagName || el.el){ // element
37632                 item = this.addElement(el);
37633             }else if(typeof el == "object"){ // must be menu item config?
37634                 item = this.addMenuItem(el);
37635             }
37636         }
37637         return item;
37638     },
37639
37640     /**
37641      * Returns this menu's underlying {@link Roo.Element} object
37642      * @return {Roo.Element} The element
37643      */
37644     getEl : function(){
37645         if(!this.el){
37646             this.render();
37647         }
37648         return this.el;
37649     },
37650
37651     /**
37652      * Adds a separator bar to the menu
37653      * @return {Roo.menu.Item} The menu item that was added
37654      */
37655     addSeparator : function(){
37656         return this.addItem(new Roo.menu.Separator());
37657     },
37658
37659     /**
37660      * Adds an {@link Roo.Element} object to the menu
37661      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37662      * @return {Roo.menu.Item} The menu item that was added
37663      */
37664     addElement : function(el){
37665         return this.addItem(new Roo.menu.BaseItem(el));
37666     },
37667
37668     /**
37669      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37670      * @param {Roo.menu.Item} item The menu item to add
37671      * @return {Roo.menu.Item} The menu item that was added
37672      */
37673     addItem : function(item){
37674         this.items.add(item);
37675         if(this.ul){
37676             var li = document.createElement("li");
37677             li.className = "x-menu-list-item";
37678             this.ul.dom.appendChild(li);
37679             item.render(li, this);
37680             this.delayAutoWidth();
37681         }
37682         return item;
37683     },
37684
37685     /**
37686      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37687      * @param {Object} config A MenuItem config object
37688      * @return {Roo.menu.Item} The menu item that was added
37689      */
37690     addMenuItem : function(config){
37691         if(!(config instanceof Roo.menu.Item)){
37692             if(typeof config.checked == "boolean"){ // must be check menu item config?
37693                 config = new Roo.menu.CheckItem(config);
37694             }else{
37695                 config = new Roo.menu.Item(config);
37696             }
37697         }
37698         return this.addItem(config);
37699     },
37700
37701     /**
37702      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37703      * @param {String} text The text to display in the menu item
37704      * @return {Roo.menu.Item} The menu item that was added
37705      */
37706     addText : function(text){
37707         return this.addItem(new Roo.menu.TextItem({ text : text }));
37708     },
37709
37710     /**
37711      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37712      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37713      * @param {Roo.menu.Item} item The menu item to add
37714      * @return {Roo.menu.Item} The menu item that was added
37715      */
37716     insert : function(index, item){
37717         this.items.insert(index, item);
37718         if(this.ul){
37719             var li = document.createElement("li");
37720             li.className = "x-menu-list-item";
37721             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37722             item.render(li, this);
37723             this.delayAutoWidth();
37724         }
37725         return item;
37726     },
37727
37728     /**
37729      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37730      * @param {Roo.menu.Item} item The menu item to remove
37731      */
37732     remove : function(item){
37733         this.items.removeKey(item.id);
37734         item.destroy();
37735     },
37736
37737     /**
37738      * Removes and destroys all items in the menu
37739      */
37740     removeAll : function(){
37741         var f;
37742         while(f = this.items.first()){
37743             this.remove(f);
37744         }
37745     }
37746 });
37747
37748 // MenuNav is a private utility class used internally by the Menu
37749 Roo.menu.MenuNav = function(menu){
37750     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37751     this.scope = this.menu = menu;
37752 };
37753
37754 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37755     doRelay : function(e, h){
37756         var k = e.getKey();
37757         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37758             this.menu.tryActivate(0, 1);
37759             return false;
37760         }
37761         return h.call(this.scope || this, e, this.menu);
37762     },
37763
37764     up : function(e, m){
37765         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37766             m.tryActivate(m.items.length-1, -1);
37767         }
37768     },
37769
37770     down : function(e, m){
37771         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37772             m.tryActivate(0, 1);
37773         }
37774     },
37775
37776     right : function(e, m){
37777         if(m.activeItem){
37778             m.activeItem.expandMenu(true);
37779         }
37780     },
37781
37782     left : function(e, m){
37783         m.hide();
37784         if(m.parentMenu && m.parentMenu.activeItem){
37785             m.parentMenu.activeItem.activate();
37786         }
37787     },
37788
37789     enter : function(e, m){
37790         if(m.activeItem){
37791             e.stopPropagation();
37792             m.activeItem.onClick(e);
37793             m.fireEvent("click", this, m.activeItem);
37794             return true;
37795         }
37796     }
37797 });/*
37798  * Based on:
37799  * Ext JS Library 1.1.1
37800  * Copyright(c) 2006-2007, Ext JS, LLC.
37801  *
37802  * Originally Released Under LGPL - original licence link has changed is not relivant.
37803  *
37804  * Fork - LGPL
37805  * <script type="text/javascript">
37806  */
37807  
37808 /**
37809  * @class Roo.menu.MenuMgr
37810  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37811  * @singleton
37812  */
37813 Roo.menu.MenuMgr = function(){
37814    var menus, active, groups = {}, attached = false, lastShow = new Date();
37815
37816    // private - called when first menu is created
37817    function init(){
37818        menus = {};
37819        active = new Roo.util.MixedCollection();
37820        Roo.get(document).addKeyListener(27, function(){
37821            if(active.length > 0){
37822                hideAll();
37823            }
37824        });
37825    }
37826
37827    // private
37828    function hideAll(){
37829        if(active && active.length > 0){
37830            var c = active.clone();
37831            c.each(function(m){
37832                m.hide();
37833            });
37834        }
37835    }
37836
37837    // private
37838    function onHide(m){
37839        active.remove(m);
37840        if(active.length < 1){
37841            Roo.get(document).un("mousedown", onMouseDown);
37842            attached = false;
37843        }
37844    }
37845
37846    // private
37847    function onShow(m){
37848        var last = active.last();
37849        lastShow = new Date();
37850        active.add(m);
37851        if(!attached){
37852            Roo.get(document).on("mousedown", onMouseDown);
37853            attached = true;
37854        }
37855        if(m.parentMenu){
37856           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37857           m.parentMenu.activeChild = m;
37858        }else if(last && last.isVisible()){
37859           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37860        }
37861    }
37862
37863    // private
37864    function onBeforeHide(m){
37865        if(m.activeChild){
37866            m.activeChild.hide();
37867        }
37868        if(m.autoHideTimer){
37869            clearTimeout(m.autoHideTimer);
37870            delete m.autoHideTimer;
37871        }
37872    }
37873
37874    // private
37875    function onBeforeShow(m){
37876        var pm = m.parentMenu;
37877        if(!pm && !m.allowOtherMenus){
37878            hideAll();
37879        }else if(pm && pm.activeChild && active != m){
37880            pm.activeChild.hide();
37881        }
37882    }
37883
37884    // private
37885    function onMouseDown(e){
37886        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37887            hideAll();
37888        }
37889    }
37890
37891    // private
37892    function onBeforeCheck(mi, state){
37893        if(state){
37894            var g = groups[mi.group];
37895            for(var i = 0, l = g.length; i < l; i++){
37896                if(g[i] != mi){
37897                    g[i].setChecked(false);
37898                }
37899            }
37900        }
37901    }
37902
37903    return {
37904
37905        /**
37906         * Hides all menus that are currently visible
37907         */
37908        hideAll : function(){
37909             hideAll();  
37910        },
37911
37912        // private
37913        register : function(menu){
37914            if(!menus){
37915                init();
37916            }
37917            menus[menu.id] = menu;
37918            menu.on("beforehide", onBeforeHide);
37919            menu.on("hide", onHide);
37920            menu.on("beforeshow", onBeforeShow);
37921            menu.on("show", onShow);
37922            var g = menu.group;
37923            if(g && menu.events["checkchange"]){
37924                if(!groups[g]){
37925                    groups[g] = [];
37926                }
37927                groups[g].push(menu);
37928                menu.on("checkchange", onCheck);
37929            }
37930        },
37931
37932         /**
37933          * Returns a {@link Roo.menu.Menu} object
37934          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37935          * be used to generate and return a new Menu instance.
37936          */
37937        get : function(menu){
37938            if(typeof menu == "string"){ // menu id
37939                return menus[menu];
37940            }else if(menu.events){  // menu instance
37941                return menu;
37942            }else if(typeof menu.length == 'number'){ // array of menu items?
37943                return new Roo.menu.Menu({items:menu});
37944            }else{ // otherwise, must be a config
37945                return new Roo.menu.Menu(menu);
37946            }
37947        },
37948
37949        // private
37950        unregister : function(menu){
37951            delete menus[menu.id];
37952            menu.un("beforehide", onBeforeHide);
37953            menu.un("hide", onHide);
37954            menu.un("beforeshow", onBeforeShow);
37955            menu.un("show", onShow);
37956            var g = menu.group;
37957            if(g && menu.events["checkchange"]){
37958                groups[g].remove(menu);
37959                menu.un("checkchange", onCheck);
37960            }
37961        },
37962
37963        // private
37964        registerCheckable : function(menuItem){
37965            var g = menuItem.group;
37966            if(g){
37967                if(!groups[g]){
37968                    groups[g] = [];
37969                }
37970                groups[g].push(menuItem);
37971                menuItem.on("beforecheckchange", onBeforeCheck);
37972            }
37973        },
37974
37975        // private
37976        unregisterCheckable : function(menuItem){
37977            var g = menuItem.group;
37978            if(g){
37979                groups[g].remove(menuItem);
37980                menuItem.un("beforecheckchange", onBeforeCheck);
37981            }
37982        }
37983    };
37984 }();/*
37985  * Based on:
37986  * Ext JS Library 1.1.1
37987  * Copyright(c) 2006-2007, Ext JS, LLC.
37988  *
37989  * Originally Released Under LGPL - original licence link has changed is not relivant.
37990  *
37991  * Fork - LGPL
37992  * <script type="text/javascript">
37993  */
37994  
37995
37996 /**
37997  * @class Roo.menu.BaseItem
37998  * @extends Roo.Component
37999  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38000  * management and base configuration options shared by all menu components.
38001  * @constructor
38002  * Creates a new BaseItem
38003  * @param {Object} config Configuration options
38004  */
38005 Roo.menu.BaseItem = function(config){
38006     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38007
38008     this.addEvents({
38009         /**
38010          * @event click
38011          * Fires when this item is clicked
38012          * @param {Roo.menu.BaseItem} this
38013          * @param {Roo.EventObject} e
38014          */
38015         click: true,
38016         /**
38017          * @event activate
38018          * Fires when this item is activated
38019          * @param {Roo.menu.BaseItem} this
38020          */
38021         activate : true,
38022         /**
38023          * @event deactivate
38024          * Fires when this item is deactivated
38025          * @param {Roo.menu.BaseItem} this
38026          */
38027         deactivate : true
38028     });
38029
38030     if(this.handler){
38031         this.on("click", this.handler, this.scope, true);
38032     }
38033 };
38034
38035 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38036     /**
38037      * @cfg {Function} handler
38038      * A function that will handle the click event of this menu item (defaults to undefined)
38039      */
38040     /**
38041      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38042      */
38043     canActivate : false,
38044     
38045      /**
38046      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38047      */
38048     hidden: false,
38049     
38050     /**
38051      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38052      */
38053     activeClass : "x-menu-item-active",
38054     /**
38055      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38056      */
38057     hideOnClick : true,
38058     /**
38059      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38060      */
38061     hideDelay : 100,
38062
38063     // private
38064     ctype: "Roo.menu.BaseItem",
38065
38066     // private
38067     actionMode : "container",
38068
38069     // private
38070     render : function(container, parentMenu){
38071         this.parentMenu = parentMenu;
38072         Roo.menu.BaseItem.superclass.render.call(this, container);
38073         this.container.menuItemId = this.id;
38074     },
38075
38076     // private
38077     onRender : function(container, position){
38078         this.el = Roo.get(this.el);
38079         container.dom.appendChild(this.el.dom);
38080     },
38081
38082     // private
38083     onClick : function(e){
38084         if(!this.disabled && this.fireEvent("click", this, e) !== false
38085                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38086             this.handleClick(e);
38087         }else{
38088             e.stopEvent();
38089         }
38090     },
38091
38092     // private
38093     activate : function(){
38094         if(this.disabled){
38095             return false;
38096         }
38097         var li = this.container;
38098         li.addClass(this.activeClass);
38099         this.region = li.getRegion().adjust(2, 2, -2, -2);
38100         this.fireEvent("activate", this);
38101         return true;
38102     },
38103
38104     // private
38105     deactivate : function(){
38106         this.container.removeClass(this.activeClass);
38107         this.fireEvent("deactivate", this);
38108     },
38109
38110     // private
38111     shouldDeactivate : function(e){
38112         return !this.region || !this.region.contains(e.getPoint());
38113     },
38114
38115     // private
38116     handleClick : function(e){
38117         if(this.hideOnClick){
38118             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38119         }
38120     },
38121
38122     // private
38123     expandMenu : function(autoActivate){
38124         // do nothing
38125     },
38126
38127     // private
38128     hideMenu : function(){
38129         // do nothing
38130     }
38131 });/*
38132  * Based on:
38133  * Ext JS Library 1.1.1
38134  * Copyright(c) 2006-2007, Ext JS, LLC.
38135  *
38136  * Originally Released Under LGPL - original licence link has changed is not relivant.
38137  *
38138  * Fork - LGPL
38139  * <script type="text/javascript">
38140  */
38141  
38142 /**
38143  * @class Roo.menu.Adapter
38144  * @extends Roo.menu.BaseItem
38145  * 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.
38146  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38147  * @constructor
38148  * Creates a new Adapter
38149  * @param {Object} config Configuration options
38150  */
38151 Roo.menu.Adapter = function(component, config){
38152     Roo.menu.Adapter.superclass.constructor.call(this, config);
38153     this.component = component;
38154 };
38155 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38156     // private
38157     canActivate : true,
38158
38159     // private
38160     onRender : function(container, position){
38161         this.component.render(container);
38162         this.el = this.component.getEl();
38163     },
38164
38165     // private
38166     activate : function(){
38167         if(this.disabled){
38168             return false;
38169         }
38170         this.component.focus();
38171         this.fireEvent("activate", this);
38172         return true;
38173     },
38174
38175     // private
38176     deactivate : function(){
38177         this.fireEvent("deactivate", this);
38178     },
38179
38180     // private
38181     disable : function(){
38182         this.component.disable();
38183         Roo.menu.Adapter.superclass.disable.call(this);
38184     },
38185
38186     // private
38187     enable : function(){
38188         this.component.enable();
38189         Roo.menu.Adapter.superclass.enable.call(this);
38190     }
38191 });/*
38192  * Based on:
38193  * Ext JS Library 1.1.1
38194  * Copyright(c) 2006-2007, Ext JS, LLC.
38195  *
38196  * Originally Released Under LGPL - original licence link has changed is not relivant.
38197  *
38198  * Fork - LGPL
38199  * <script type="text/javascript">
38200  */
38201
38202 /**
38203  * @class Roo.menu.TextItem
38204  * @extends Roo.menu.BaseItem
38205  * Adds a static text string to a menu, usually used as either a heading or group separator.
38206  * Note: old style constructor with text is still supported.
38207  * 
38208  * @constructor
38209  * Creates a new TextItem
38210  * @param {Object} cfg Configuration
38211  */
38212 Roo.menu.TextItem = function(cfg){
38213     if (typeof(cfg) == 'string') {
38214         this.text = cfg;
38215     } else {
38216         Roo.apply(this,cfg);
38217     }
38218     
38219     Roo.menu.TextItem.superclass.constructor.call(this);
38220 };
38221
38222 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38223     /**
38224      * @cfg {Boolean} text Text to show on item.
38225      */
38226     text : '',
38227     
38228     /**
38229      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38230      */
38231     hideOnClick : false,
38232     /**
38233      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38234      */
38235     itemCls : "x-menu-text",
38236
38237     // private
38238     onRender : function(){
38239         var s = document.createElement("span");
38240         s.className = this.itemCls;
38241         s.innerHTML = this.text;
38242         this.el = s;
38243         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38244     }
38245 });/*
38246  * Based on:
38247  * Ext JS Library 1.1.1
38248  * Copyright(c) 2006-2007, Ext JS, LLC.
38249  *
38250  * Originally Released Under LGPL - original licence link has changed is not relivant.
38251  *
38252  * Fork - LGPL
38253  * <script type="text/javascript">
38254  */
38255
38256 /**
38257  * @class Roo.menu.Separator
38258  * @extends Roo.menu.BaseItem
38259  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38260  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38261  * @constructor
38262  * @param {Object} config Configuration options
38263  */
38264 Roo.menu.Separator = function(config){
38265     Roo.menu.Separator.superclass.constructor.call(this, config);
38266 };
38267
38268 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38269     /**
38270      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38271      */
38272     itemCls : "x-menu-sep",
38273     /**
38274      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38275      */
38276     hideOnClick : false,
38277
38278     // private
38279     onRender : function(li){
38280         var s = document.createElement("span");
38281         s.className = this.itemCls;
38282         s.innerHTML = "&#160;";
38283         this.el = s;
38284         li.addClass("x-menu-sep-li");
38285         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38286     }
38287 });/*
38288  * Based on:
38289  * Ext JS Library 1.1.1
38290  * Copyright(c) 2006-2007, Ext JS, LLC.
38291  *
38292  * Originally Released Under LGPL - original licence link has changed is not relivant.
38293  *
38294  * Fork - LGPL
38295  * <script type="text/javascript">
38296  */
38297 /**
38298  * @class Roo.menu.Item
38299  * @extends Roo.menu.BaseItem
38300  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38301  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38302  * activation and click handling.
38303  * @constructor
38304  * Creates a new Item
38305  * @param {Object} config Configuration options
38306  */
38307 Roo.menu.Item = function(config){
38308     Roo.menu.Item.superclass.constructor.call(this, config);
38309     if(this.menu){
38310         this.menu = Roo.menu.MenuMgr.get(this.menu);
38311     }
38312 };
38313 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38314     
38315     /**
38316      * @cfg {String} text
38317      * The text to show on the menu item.
38318      */
38319     text: '',
38320      /**
38321      * @cfg {String} HTML to render in menu
38322      * The text to show on the menu item (HTML version).
38323      */
38324     html: '',
38325     /**
38326      * @cfg {String} icon
38327      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38328      */
38329     icon: undefined,
38330     /**
38331      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38332      */
38333     itemCls : "x-menu-item",
38334     /**
38335      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38336      */
38337     canActivate : true,
38338     /**
38339      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38340      */
38341     showDelay: 200,
38342     // doc'd in BaseItem
38343     hideDelay: 200,
38344
38345     // private
38346     ctype: "Roo.menu.Item",
38347     
38348     // private
38349     onRender : function(container, position){
38350         var el = document.createElement("a");
38351         el.hideFocus = true;
38352         el.unselectable = "on";
38353         el.href = this.href || "#";
38354         if(this.hrefTarget){
38355             el.target = this.hrefTarget;
38356         }
38357         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38358         
38359         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38360         
38361         el.innerHTML = String.format(
38362                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38363                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38364         this.el = el;
38365         Roo.menu.Item.superclass.onRender.call(this, container, position);
38366     },
38367
38368     /**
38369      * Sets the text to display in this menu item
38370      * @param {String} text The text to display
38371      * @param {Boolean} isHTML true to indicate text is pure html.
38372      */
38373     setText : function(text, isHTML){
38374         if (isHTML) {
38375             this.html = text;
38376         } else {
38377             this.text = text;
38378             this.html = '';
38379         }
38380         if(this.rendered){
38381             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38382      
38383             this.el.update(String.format(
38384                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38385                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38386             this.parentMenu.autoWidth();
38387         }
38388     },
38389
38390     // private
38391     handleClick : function(e){
38392         if(!this.href){ // if no link defined, stop the event automatically
38393             e.stopEvent();
38394         }
38395         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38396     },
38397
38398     // private
38399     activate : function(autoExpand){
38400         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38401             this.focus();
38402             if(autoExpand){
38403                 this.expandMenu();
38404             }
38405         }
38406         return true;
38407     },
38408
38409     // private
38410     shouldDeactivate : function(e){
38411         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38412             if(this.menu && this.menu.isVisible()){
38413                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38414             }
38415             return true;
38416         }
38417         return false;
38418     },
38419
38420     // private
38421     deactivate : function(){
38422         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38423         this.hideMenu();
38424     },
38425
38426     // private
38427     expandMenu : function(autoActivate){
38428         if(!this.disabled && this.menu){
38429             clearTimeout(this.hideTimer);
38430             delete this.hideTimer;
38431             if(!this.menu.isVisible() && !this.showTimer){
38432                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38433             }else if (this.menu.isVisible() && autoActivate){
38434                 this.menu.tryActivate(0, 1);
38435             }
38436         }
38437     },
38438
38439     // private
38440     deferExpand : function(autoActivate){
38441         delete this.showTimer;
38442         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38443         if(autoActivate){
38444             this.menu.tryActivate(0, 1);
38445         }
38446     },
38447
38448     // private
38449     hideMenu : function(){
38450         clearTimeout(this.showTimer);
38451         delete this.showTimer;
38452         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38453             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38454         }
38455     },
38456
38457     // private
38458     deferHide : function(){
38459         delete this.hideTimer;
38460         this.menu.hide();
38461     }
38462 });/*
38463  * Based on:
38464  * Ext JS Library 1.1.1
38465  * Copyright(c) 2006-2007, Ext JS, LLC.
38466  *
38467  * Originally Released Under LGPL - original licence link has changed is not relivant.
38468  *
38469  * Fork - LGPL
38470  * <script type="text/javascript">
38471  */
38472  
38473 /**
38474  * @class Roo.menu.CheckItem
38475  * @extends Roo.menu.Item
38476  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38477  * @constructor
38478  * Creates a new CheckItem
38479  * @param {Object} config Configuration options
38480  */
38481 Roo.menu.CheckItem = function(config){
38482     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38483     this.addEvents({
38484         /**
38485          * @event beforecheckchange
38486          * Fires before the checked value is set, providing an opportunity to cancel if needed
38487          * @param {Roo.menu.CheckItem} this
38488          * @param {Boolean} checked The new checked value that will be set
38489          */
38490         "beforecheckchange" : true,
38491         /**
38492          * @event checkchange
38493          * Fires after the checked value has been set
38494          * @param {Roo.menu.CheckItem} this
38495          * @param {Boolean} checked The checked value that was set
38496          */
38497         "checkchange" : true
38498     });
38499     if(this.checkHandler){
38500         this.on('checkchange', this.checkHandler, this.scope);
38501     }
38502 };
38503 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38504     /**
38505      * @cfg {String} group
38506      * All check items with the same group name will automatically be grouped into a single-select
38507      * radio button group (defaults to '')
38508      */
38509     /**
38510      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38511      */
38512     itemCls : "x-menu-item x-menu-check-item",
38513     /**
38514      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38515      */
38516     groupClass : "x-menu-group-item",
38517
38518     /**
38519      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38520      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38521      * initialized with checked = true will be rendered as checked.
38522      */
38523     checked: false,
38524
38525     // private
38526     ctype: "Roo.menu.CheckItem",
38527
38528     // private
38529     onRender : function(c){
38530         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38531         if(this.group){
38532             this.el.addClass(this.groupClass);
38533         }
38534         Roo.menu.MenuMgr.registerCheckable(this);
38535         if(this.checked){
38536             this.checked = false;
38537             this.setChecked(true, true);
38538         }
38539     },
38540
38541     // private
38542     destroy : function(){
38543         if(this.rendered){
38544             Roo.menu.MenuMgr.unregisterCheckable(this);
38545         }
38546         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38547     },
38548
38549     /**
38550      * Set the checked state of this item
38551      * @param {Boolean} checked The new checked value
38552      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38553      */
38554     setChecked : function(state, suppressEvent){
38555         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38556             if(this.container){
38557                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38558             }
38559             this.checked = state;
38560             if(suppressEvent !== true){
38561                 this.fireEvent("checkchange", this, state);
38562             }
38563         }
38564     },
38565
38566     // private
38567     handleClick : function(e){
38568        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38569            this.setChecked(!this.checked);
38570        }
38571        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38572     }
38573 });/*
38574  * Based on:
38575  * Ext JS Library 1.1.1
38576  * Copyright(c) 2006-2007, Ext JS, LLC.
38577  *
38578  * Originally Released Under LGPL - original licence link has changed is not relivant.
38579  *
38580  * Fork - LGPL
38581  * <script type="text/javascript">
38582  */
38583  
38584 /**
38585  * @class Roo.menu.DateItem
38586  * @extends Roo.menu.Adapter
38587  * A menu item that wraps the {@link Roo.DatPicker} component.
38588  * @constructor
38589  * Creates a new DateItem
38590  * @param {Object} config Configuration options
38591  */
38592 Roo.menu.DateItem = function(config){
38593     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38594     /** The Roo.DatePicker object @type Roo.DatePicker */
38595     this.picker = this.component;
38596     this.addEvents({select: true});
38597     
38598     this.picker.on("render", function(picker){
38599         picker.getEl().swallowEvent("click");
38600         picker.container.addClass("x-menu-date-item");
38601     });
38602
38603     this.picker.on("select", this.onSelect, this);
38604 };
38605
38606 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38607     // private
38608     onSelect : function(picker, date){
38609         this.fireEvent("select", this, date, picker);
38610         Roo.menu.DateItem.superclass.handleClick.call(this);
38611     }
38612 });/*
38613  * Based on:
38614  * Ext JS Library 1.1.1
38615  * Copyright(c) 2006-2007, Ext JS, LLC.
38616  *
38617  * Originally Released Under LGPL - original licence link has changed is not relivant.
38618  *
38619  * Fork - LGPL
38620  * <script type="text/javascript">
38621  */
38622  
38623 /**
38624  * @class Roo.menu.ColorItem
38625  * @extends Roo.menu.Adapter
38626  * A menu item that wraps the {@link Roo.ColorPalette} component.
38627  * @constructor
38628  * Creates a new ColorItem
38629  * @param {Object} config Configuration options
38630  */
38631 Roo.menu.ColorItem = function(config){
38632     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38633     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38634     this.palette = this.component;
38635     this.relayEvents(this.palette, ["select"]);
38636     if(this.selectHandler){
38637         this.on('select', this.selectHandler, this.scope);
38638     }
38639 };
38640 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38641  * Based on:
38642  * Ext JS Library 1.1.1
38643  * Copyright(c) 2006-2007, Ext JS, LLC.
38644  *
38645  * Originally Released Under LGPL - original licence link has changed is not relivant.
38646  *
38647  * Fork - LGPL
38648  * <script type="text/javascript">
38649  */
38650  
38651
38652 /**
38653  * @class Roo.menu.DateMenu
38654  * @extends Roo.menu.Menu
38655  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38656  * @constructor
38657  * Creates a new DateMenu
38658  * @param {Object} config Configuration options
38659  */
38660 Roo.menu.DateMenu = function(config){
38661     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38662     this.plain = true;
38663     var di = new Roo.menu.DateItem(config);
38664     this.add(di);
38665     /**
38666      * The {@link Roo.DatePicker} instance for this DateMenu
38667      * @type DatePicker
38668      */
38669     this.picker = di.picker;
38670     /**
38671      * @event select
38672      * @param {DatePicker} picker
38673      * @param {Date} date
38674      */
38675     this.relayEvents(di, ["select"]);
38676     this.on('beforeshow', function(){
38677         if(this.picker){
38678             this.picker.hideMonthPicker(false);
38679         }
38680     }, this);
38681 };
38682 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38683     cls:'x-date-menu'
38684 });/*
38685  * Based on:
38686  * Ext JS Library 1.1.1
38687  * Copyright(c) 2006-2007, Ext JS, LLC.
38688  *
38689  * Originally Released Under LGPL - original licence link has changed is not relivant.
38690  *
38691  * Fork - LGPL
38692  * <script type="text/javascript">
38693  */
38694  
38695
38696 /**
38697  * @class Roo.menu.ColorMenu
38698  * @extends Roo.menu.Menu
38699  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38700  * @constructor
38701  * Creates a new ColorMenu
38702  * @param {Object} config Configuration options
38703  */
38704 Roo.menu.ColorMenu = function(config){
38705     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38706     this.plain = true;
38707     var ci = new Roo.menu.ColorItem(config);
38708     this.add(ci);
38709     /**
38710      * The {@link Roo.ColorPalette} instance for this ColorMenu
38711      * @type ColorPalette
38712      */
38713     this.palette = ci.palette;
38714     /**
38715      * @event select
38716      * @param {ColorPalette} palette
38717      * @param {String} color
38718      */
38719     this.relayEvents(ci, ["select"]);
38720 };
38721 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38722  * Based on:
38723  * Ext JS Library 1.1.1
38724  * Copyright(c) 2006-2007, Ext JS, LLC.
38725  *
38726  * Originally Released Under LGPL - original licence link has changed is not relivant.
38727  *
38728  * Fork - LGPL
38729  * <script type="text/javascript">
38730  */
38731  
38732 /**
38733  * @class Roo.form.TextItem
38734  * @extends Roo.BoxComponent
38735  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38736  * @constructor
38737  * Creates a new TextItem
38738  * @param {Object} config Configuration options
38739  */
38740 Roo.form.TextItem = function(config){
38741     Roo.form.TextItem.superclass.constructor.call(this, config);
38742 };
38743
38744 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38745     
38746     /**
38747      * @cfg {String} tag the tag for this item (default div)
38748      */
38749     tag : 'div',
38750     /**
38751      * @cfg {String} html the content for this item
38752      */
38753     html : '',
38754     
38755     getAutoCreate : function()
38756     {
38757         var cfg = {
38758             id: this.id,
38759             tag: this.tag,
38760             html: this.html,
38761             cls: 'x-form-item'
38762         };
38763         
38764         return cfg;
38765         
38766     },
38767     
38768     onRender : function(ct, position)
38769     {
38770         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38771         
38772         if(!this.el){
38773             var cfg = this.getAutoCreate();
38774             if(!cfg.name){
38775                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38776             }
38777             if (!cfg.name.length) {
38778                 delete cfg.name;
38779             }
38780             this.el = ct.createChild(cfg, position);
38781         }
38782     }
38783     
38784 });/*
38785  * Based on:
38786  * Ext JS Library 1.1.1
38787  * Copyright(c) 2006-2007, Ext JS, LLC.
38788  *
38789  * Originally Released Under LGPL - original licence link has changed is not relivant.
38790  *
38791  * Fork - LGPL
38792  * <script type="text/javascript">
38793  */
38794  
38795 /**
38796  * @class Roo.form.Field
38797  * @extends Roo.BoxComponent
38798  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38799  * @constructor
38800  * Creates a new Field
38801  * @param {Object} config Configuration options
38802  */
38803 Roo.form.Field = function(config){
38804     Roo.form.Field.superclass.constructor.call(this, config);
38805 };
38806
38807 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38808     /**
38809      * @cfg {String} fieldLabel Label to use when rendering a form.
38810      */
38811        /**
38812      * @cfg {String} qtip Mouse over tip
38813      */
38814      
38815     /**
38816      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38817      */
38818     invalidClass : "x-form-invalid",
38819     /**
38820      * @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")
38821      */
38822     invalidText : "The value in this field is invalid",
38823     /**
38824      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38825      */
38826     focusClass : "x-form-focus",
38827     /**
38828      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38829       automatic validation (defaults to "keyup").
38830      */
38831     validationEvent : "keyup",
38832     /**
38833      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38834      */
38835     validateOnBlur : true,
38836     /**
38837      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38838      */
38839     validationDelay : 250,
38840     /**
38841      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38842      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38843      */
38844     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38845     /**
38846      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38847      */
38848     fieldClass : "x-form-field",
38849     /**
38850      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38851      *<pre>
38852 Value         Description
38853 -----------   ----------------------------------------------------------------------
38854 qtip          Display a quick tip when the user hovers over the field
38855 title         Display a default browser title attribute popup
38856 under         Add a block div beneath the field containing the error text
38857 side          Add an error icon to the right of the field with a popup on hover
38858 [element id]  Add the error text directly to the innerHTML of the specified element
38859 </pre>
38860      */
38861     msgTarget : 'qtip',
38862     /**
38863      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38864      */
38865     msgFx : 'normal',
38866
38867     /**
38868      * @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.
38869      */
38870     readOnly : false,
38871
38872     /**
38873      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38874      */
38875     disabled : false,
38876
38877     /**
38878      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38879      */
38880     inputType : undefined,
38881     
38882     /**
38883      * @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).
38884          */
38885         tabIndex : undefined,
38886         
38887     // private
38888     isFormField : true,
38889
38890     // private
38891     hasFocus : false,
38892     /**
38893      * @property {Roo.Element} fieldEl
38894      * Element Containing the rendered Field (with label etc.)
38895      */
38896     /**
38897      * @cfg {Mixed} value A value to initialize this field with.
38898      */
38899     value : undefined,
38900
38901     /**
38902      * @cfg {String} name The field's HTML name attribute.
38903      */
38904     /**
38905      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38906      */
38907     // private
38908     loadedValue : false,
38909      
38910      
38911         // private ??
38912         initComponent : function(){
38913         Roo.form.Field.superclass.initComponent.call(this);
38914         this.addEvents({
38915             /**
38916              * @event focus
38917              * Fires when this field receives input focus.
38918              * @param {Roo.form.Field} this
38919              */
38920             focus : true,
38921             /**
38922              * @event blur
38923              * Fires when this field loses input focus.
38924              * @param {Roo.form.Field} this
38925              */
38926             blur : true,
38927             /**
38928              * @event specialkey
38929              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38930              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38931              * @param {Roo.form.Field} this
38932              * @param {Roo.EventObject} e The event object
38933              */
38934             specialkey : true,
38935             /**
38936              * @event change
38937              * Fires just before the field blurs if the field value has changed.
38938              * @param {Roo.form.Field} this
38939              * @param {Mixed} newValue The new value
38940              * @param {Mixed} oldValue The original value
38941              */
38942             change : true,
38943             /**
38944              * @event invalid
38945              * Fires after the field has been marked as invalid.
38946              * @param {Roo.form.Field} this
38947              * @param {String} msg The validation message
38948              */
38949             invalid : true,
38950             /**
38951              * @event valid
38952              * Fires after the field has been validated with no errors.
38953              * @param {Roo.form.Field} this
38954              */
38955             valid : true,
38956              /**
38957              * @event keyup
38958              * Fires after the key up
38959              * @param {Roo.form.Field} this
38960              * @param {Roo.EventObject}  e The event Object
38961              */
38962             keyup : true
38963         });
38964     },
38965
38966     /**
38967      * Returns the name attribute of the field if available
38968      * @return {String} name The field name
38969      */
38970     getName: function(){
38971          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38972     },
38973
38974     // private
38975     onRender : function(ct, position){
38976         Roo.form.Field.superclass.onRender.call(this, ct, position);
38977         if(!this.el){
38978             var cfg = this.getAutoCreate();
38979             if(!cfg.name){
38980                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38981             }
38982             if (!cfg.name.length) {
38983                 delete cfg.name;
38984             }
38985             if(this.inputType){
38986                 cfg.type = this.inputType;
38987             }
38988             this.el = ct.createChild(cfg, position);
38989         }
38990         var type = this.el.dom.type;
38991         if(type){
38992             if(type == 'password'){
38993                 type = 'text';
38994             }
38995             this.el.addClass('x-form-'+type);
38996         }
38997         if(this.readOnly){
38998             this.el.dom.readOnly = true;
38999         }
39000         if(this.tabIndex !== undefined){
39001             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39002         }
39003
39004         this.el.addClass([this.fieldClass, this.cls]);
39005         this.initValue();
39006     },
39007
39008     /**
39009      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39010      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39011      * @return {Roo.form.Field} this
39012      */
39013     applyTo : function(target){
39014         this.allowDomMove = false;
39015         this.el = Roo.get(target);
39016         this.render(this.el.dom.parentNode);
39017         return this;
39018     },
39019
39020     // private
39021     initValue : function(){
39022         if(this.value !== undefined){
39023             this.setValue(this.value);
39024         }else if(this.el.dom.value.length > 0){
39025             this.setValue(this.el.dom.value);
39026         }
39027     },
39028
39029     /**
39030      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39031      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39032      */
39033     isDirty : function() {
39034         if(this.disabled) {
39035             return false;
39036         }
39037         return String(this.getValue()) !== String(this.originalValue);
39038     },
39039
39040     /**
39041      * stores the current value in loadedValue
39042      */
39043     resetHasChanged : function()
39044     {
39045         this.loadedValue = String(this.getValue());
39046     },
39047     /**
39048      * checks the current value against the 'loaded' value.
39049      * Note - will return false if 'resetHasChanged' has not been called first.
39050      */
39051     hasChanged : function()
39052     {
39053         if(this.disabled || this.readOnly) {
39054             return false;
39055         }
39056         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39057     },
39058     
39059     
39060     
39061     // private
39062     afterRender : function(){
39063         Roo.form.Field.superclass.afterRender.call(this);
39064         this.initEvents();
39065     },
39066
39067     // private
39068     fireKey : function(e){
39069         //Roo.log('field ' + e.getKey());
39070         if(e.isNavKeyPress()){
39071             this.fireEvent("specialkey", this, e);
39072         }
39073     },
39074
39075     /**
39076      * Resets the current field value to the originally loaded value and clears any validation messages
39077      */
39078     reset : function(){
39079         this.setValue(this.resetValue);
39080         this.originalValue = this.getValue();
39081         this.clearInvalid();
39082     },
39083
39084     // private
39085     initEvents : function(){
39086         // safari killled keypress - so keydown is now used..
39087         this.el.on("keydown" , this.fireKey,  this);
39088         this.el.on("focus", this.onFocus,  this);
39089         this.el.on("blur", this.onBlur,  this);
39090         this.el.relayEvent('keyup', this);
39091
39092         // reference to original value for reset
39093         this.originalValue = this.getValue();
39094         this.resetValue =  this.getValue();
39095     },
39096
39097     // private
39098     onFocus : function(){
39099         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39100             this.el.addClass(this.focusClass);
39101         }
39102         if(!this.hasFocus){
39103             this.hasFocus = true;
39104             this.startValue = this.getValue();
39105             this.fireEvent("focus", this);
39106         }
39107     },
39108
39109     beforeBlur : Roo.emptyFn,
39110
39111     // private
39112     onBlur : function(){
39113         this.beforeBlur();
39114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39115             this.el.removeClass(this.focusClass);
39116         }
39117         this.hasFocus = false;
39118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39119             this.validate();
39120         }
39121         var v = this.getValue();
39122         if(String(v) !== String(this.startValue)){
39123             this.fireEvent('change', this, v, this.startValue);
39124         }
39125         this.fireEvent("blur", this);
39126     },
39127
39128     /**
39129      * Returns whether or not the field value is currently valid
39130      * @param {Boolean} preventMark True to disable marking the field invalid
39131      * @return {Boolean} True if the value is valid, else false
39132      */
39133     isValid : function(preventMark){
39134         if(this.disabled){
39135             return true;
39136         }
39137         var restore = this.preventMark;
39138         this.preventMark = preventMark === true;
39139         var v = this.validateValue(this.processValue(this.getRawValue()));
39140         this.preventMark = restore;
39141         return v;
39142     },
39143
39144     /**
39145      * Validates the field value
39146      * @return {Boolean} True if the value is valid, else false
39147      */
39148     validate : function(){
39149         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39150             this.clearInvalid();
39151             return true;
39152         }
39153         return false;
39154     },
39155
39156     processValue : function(value){
39157         return value;
39158     },
39159
39160     // private
39161     // Subclasses should provide the validation implementation by overriding this
39162     validateValue : function(value){
39163         return true;
39164     },
39165
39166     /**
39167      * Mark this field as invalid
39168      * @param {String} msg The validation message
39169      */
39170     markInvalid : function(msg){
39171         if(!this.rendered || this.preventMark){ // not rendered
39172             return;
39173         }
39174         
39175         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39176         
39177         obj.el.addClass(this.invalidClass);
39178         msg = msg || this.invalidText;
39179         switch(this.msgTarget){
39180             case 'qtip':
39181                 obj.el.dom.qtip = msg;
39182                 obj.el.dom.qclass = 'x-form-invalid-tip';
39183                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39184                     Roo.QuickTips.enable();
39185                 }
39186                 break;
39187             case 'title':
39188                 this.el.dom.title = msg;
39189                 break;
39190             case 'under':
39191                 if(!this.errorEl){
39192                     var elp = this.el.findParent('.x-form-element', 5, true);
39193                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39194                     this.errorEl.setWidth(elp.getWidth(true)-20);
39195                 }
39196                 this.errorEl.update(msg);
39197                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39198                 break;
39199             case 'side':
39200                 if(!this.errorIcon){
39201                     var elp = this.el.findParent('.x-form-element', 5, true);
39202                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39203                 }
39204                 this.alignErrorIcon();
39205                 this.errorIcon.dom.qtip = msg;
39206                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39207                 this.errorIcon.show();
39208                 this.on('resize', this.alignErrorIcon, this);
39209                 break;
39210             default:
39211                 var t = Roo.getDom(this.msgTarget);
39212                 t.innerHTML = msg;
39213                 t.style.display = this.msgDisplay;
39214                 break;
39215         }
39216         this.fireEvent('invalid', this, msg);
39217     },
39218
39219     // private
39220     alignErrorIcon : function(){
39221         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39222     },
39223
39224     /**
39225      * Clear any invalid styles/messages for this field
39226      */
39227     clearInvalid : function(){
39228         if(!this.rendered || this.preventMark){ // not rendered
39229             return;
39230         }
39231         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39232         
39233         obj.el.removeClass(this.invalidClass);
39234         switch(this.msgTarget){
39235             case 'qtip':
39236                 obj.el.dom.qtip = '';
39237                 break;
39238             case 'title':
39239                 this.el.dom.title = '';
39240                 break;
39241             case 'under':
39242                 if(this.errorEl){
39243                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39244                 }
39245                 break;
39246             case 'side':
39247                 if(this.errorIcon){
39248                     this.errorIcon.dom.qtip = '';
39249                     this.errorIcon.hide();
39250                     this.un('resize', this.alignErrorIcon, this);
39251                 }
39252                 break;
39253             default:
39254                 var t = Roo.getDom(this.msgTarget);
39255                 t.innerHTML = '';
39256                 t.style.display = 'none';
39257                 break;
39258         }
39259         this.fireEvent('valid', this);
39260     },
39261
39262     /**
39263      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39264      * @return {Mixed} value The field value
39265      */
39266     getRawValue : function(){
39267         var v = this.el.getValue();
39268         
39269         return v;
39270     },
39271
39272     /**
39273      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39274      * @return {Mixed} value The field value
39275      */
39276     getValue : function(){
39277         var v = this.el.getValue();
39278          
39279         return v;
39280     },
39281
39282     /**
39283      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39284      * @param {Mixed} value The value to set
39285      */
39286     setRawValue : function(v){
39287         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39288     },
39289
39290     /**
39291      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39292      * @param {Mixed} value The value to set
39293      */
39294     setValue : function(v){
39295         this.value = v;
39296         if(this.rendered){
39297             this.el.dom.value = (v === null || v === undefined ? '' : v);
39298              this.validate();
39299         }
39300     },
39301
39302     adjustSize : function(w, h){
39303         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39304         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39305         return s;
39306     },
39307
39308     adjustWidth : function(tag, w){
39309         tag = tag.toLowerCase();
39310         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39311             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39312                 if(tag == 'input'){
39313                     return w + 2;
39314                 }
39315                 if(tag == 'textarea'){
39316                     return w-2;
39317                 }
39318             }else if(Roo.isOpera){
39319                 if(tag == 'input'){
39320                     return w + 2;
39321                 }
39322                 if(tag == 'textarea'){
39323                     return w-2;
39324                 }
39325             }
39326         }
39327         return w;
39328     }
39329 });
39330
39331
39332 // anything other than normal should be considered experimental
39333 Roo.form.Field.msgFx = {
39334     normal : {
39335         show: function(msgEl, f){
39336             msgEl.setDisplayed('block');
39337         },
39338
39339         hide : function(msgEl, f){
39340             msgEl.setDisplayed(false).update('');
39341         }
39342     },
39343
39344     slide : {
39345         show: function(msgEl, f){
39346             msgEl.slideIn('t', {stopFx:true});
39347         },
39348
39349         hide : function(msgEl, f){
39350             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39351         }
39352     },
39353
39354     slideRight : {
39355         show: function(msgEl, f){
39356             msgEl.fixDisplay();
39357             msgEl.alignTo(f.el, 'tl-tr');
39358             msgEl.slideIn('l', {stopFx:true});
39359         },
39360
39361         hide : function(msgEl, f){
39362             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39363         }
39364     }
39365 };/*
39366  * Based on:
39367  * Ext JS Library 1.1.1
39368  * Copyright(c) 2006-2007, Ext JS, LLC.
39369  *
39370  * Originally Released Under LGPL - original licence link has changed is not relivant.
39371  *
39372  * Fork - LGPL
39373  * <script type="text/javascript">
39374  */
39375  
39376
39377 /**
39378  * @class Roo.form.TextField
39379  * @extends Roo.form.Field
39380  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39381  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39382  * @constructor
39383  * Creates a new TextField
39384  * @param {Object} config Configuration options
39385  */
39386 Roo.form.TextField = function(config){
39387     Roo.form.TextField.superclass.constructor.call(this, config);
39388     this.addEvents({
39389         /**
39390          * @event autosize
39391          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39392          * according to the default logic, but this event provides a hook for the developer to apply additional
39393          * logic at runtime to resize the field if needed.
39394              * @param {Roo.form.Field} this This text field
39395              * @param {Number} width The new field width
39396              */
39397         autosize : true
39398     });
39399 };
39400
39401 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39402     /**
39403      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39404      */
39405     grow : false,
39406     /**
39407      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39408      */
39409     growMin : 30,
39410     /**
39411      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39412      */
39413     growMax : 800,
39414     /**
39415      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39416      */
39417     vtype : null,
39418     /**
39419      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39420      */
39421     maskRe : null,
39422     /**
39423      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39424      */
39425     disableKeyFilter : false,
39426     /**
39427      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39428      */
39429     allowBlank : true,
39430     /**
39431      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39432      */
39433     minLength : 0,
39434     /**
39435      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39436      */
39437     maxLength : Number.MAX_VALUE,
39438     /**
39439      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39440      */
39441     minLengthText : "The minimum length for this field is {0}",
39442     /**
39443      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39444      */
39445     maxLengthText : "The maximum length for this field is {0}",
39446     /**
39447      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39448      */
39449     selectOnFocus : false,
39450     /**
39451      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39452      */    
39453     allowLeadingSpace : false,
39454     /**
39455      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39456      */
39457     blankText : "This field is required",
39458     /**
39459      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39460      * If available, this function will be called only after the basic validators all return true, and will be passed the
39461      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39462      */
39463     validator : null,
39464     /**
39465      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39466      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39467      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39468      */
39469     regex : null,
39470     /**
39471      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39472      */
39473     regexText : "",
39474     /**
39475      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39476      */
39477     emptyText : null,
39478    
39479
39480     // private
39481     initEvents : function()
39482     {
39483         if (this.emptyText) {
39484             this.el.attr('placeholder', this.emptyText);
39485         }
39486         
39487         Roo.form.TextField.superclass.initEvents.call(this);
39488         if(this.validationEvent == 'keyup'){
39489             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39490             this.el.on('keyup', this.filterValidation, this);
39491         }
39492         else if(this.validationEvent !== false){
39493             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39494         }
39495         
39496         if(this.selectOnFocus){
39497             this.on("focus", this.preFocus, this);
39498         }
39499         if (!this.allowLeadingSpace) {
39500             this.on('blur', this.cleanLeadingSpace, this);
39501         }
39502         
39503         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39504             this.el.on("keypress", this.filterKeys, this);
39505         }
39506         if(this.grow){
39507             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39508             this.el.on("click", this.autoSize,  this);
39509         }
39510         if(this.el.is('input[type=password]') && Roo.isSafari){
39511             this.el.on('keydown', this.SafariOnKeyDown, this);
39512         }
39513     },
39514
39515     processValue : function(value){
39516         if(this.stripCharsRe){
39517             var newValue = value.replace(this.stripCharsRe, '');
39518             if(newValue !== value){
39519                 this.setRawValue(newValue);
39520                 return newValue;
39521             }
39522         }
39523         return value;
39524     },
39525
39526     filterValidation : function(e){
39527         if(!e.isNavKeyPress()){
39528             this.validationTask.delay(this.validationDelay);
39529         }
39530     },
39531
39532     // private
39533     onKeyUp : function(e){
39534         if(!e.isNavKeyPress()){
39535             this.autoSize();
39536         }
39537     },
39538     // private - clean the leading white space
39539     cleanLeadingSpace : function(e)
39540     {
39541         if ( this.inputType == 'file') {
39542             return;
39543         }
39544         
39545         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39546     },
39547     /**
39548      * Resets the current field value to the originally-loaded value and clears any validation messages.
39549      *  
39550      */
39551     reset : function(){
39552         Roo.form.TextField.superclass.reset.call(this);
39553        
39554     }, 
39555     // private
39556     preFocus : function(){
39557         
39558         if(this.selectOnFocus){
39559             this.el.dom.select();
39560         }
39561     },
39562
39563     
39564     // private
39565     filterKeys : function(e){
39566         var k = e.getKey();
39567         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39568             return;
39569         }
39570         var c = e.getCharCode(), cc = String.fromCharCode(c);
39571         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39572             return;
39573         }
39574         if(!this.maskRe.test(cc)){
39575             e.stopEvent();
39576         }
39577     },
39578
39579     setValue : function(v){
39580         
39581         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39582         
39583         this.autoSize();
39584     },
39585
39586     /**
39587      * Validates a value according to the field's validation rules and marks the field as invalid
39588      * if the validation fails
39589      * @param {Mixed} value The value to validate
39590      * @return {Boolean} True if the value is valid, else false
39591      */
39592     validateValue : function(value){
39593         if(value.length < 1)  { // if it's blank
39594              if(this.allowBlank){
39595                 this.clearInvalid();
39596                 return true;
39597              }else{
39598                 this.markInvalid(this.blankText);
39599                 return false;
39600              }
39601         }
39602         if(value.length < this.minLength){
39603             this.markInvalid(String.format(this.minLengthText, this.minLength));
39604             return false;
39605         }
39606         if(value.length > this.maxLength){
39607             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39608             return false;
39609         }
39610         if(this.vtype){
39611             var vt = Roo.form.VTypes;
39612             if(!vt[this.vtype](value, this)){
39613                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39614                 return false;
39615             }
39616         }
39617         if(typeof this.validator == "function"){
39618             var msg = this.validator(value);
39619             if(msg !== true){
39620                 this.markInvalid(msg);
39621                 return false;
39622             }
39623         }
39624         if(this.regex && !this.regex.test(value)){
39625             this.markInvalid(this.regexText);
39626             return false;
39627         }
39628         return true;
39629     },
39630
39631     /**
39632      * Selects text in this field
39633      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39634      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39635      */
39636     selectText : function(start, end){
39637         var v = this.getRawValue();
39638         if(v.length > 0){
39639             start = start === undefined ? 0 : start;
39640             end = end === undefined ? v.length : end;
39641             var d = this.el.dom;
39642             if(d.setSelectionRange){
39643                 d.setSelectionRange(start, end);
39644             }else if(d.createTextRange){
39645                 var range = d.createTextRange();
39646                 range.moveStart("character", start);
39647                 range.moveEnd("character", v.length-end);
39648                 range.select();
39649             }
39650         }
39651     },
39652
39653     /**
39654      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39655      * This only takes effect if grow = true, and fires the autosize event.
39656      */
39657     autoSize : function(){
39658         if(!this.grow || !this.rendered){
39659             return;
39660         }
39661         if(!this.metrics){
39662             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39663         }
39664         var el = this.el;
39665         var v = el.dom.value;
39666         var d = document.createElement('div');
39667         d.appendChild(document.createTextNode(v));
39668         v = d.innerHTML;
39669         d = null;
39670         v += "&#160;";
39671         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39672         this.el.setWidth(w);
39673         this.fireEvent("autosize", this, w);
39674     },
39675     
39676     // private
39677     SafariOnKeyDown : function(event)
39678     {
39679         // this is a workaround for a password hang bug on chrome/ webkit.
39680         
39681         var isSelectAll = false;
39682         
39683         if(this.el.dom.selectionEnd > 0){
39684             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39685         }
39686         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39687             event.preventDefault();
39688             this.setValue('');
39689             return;
39690         }
39691         
39692         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39693             
39694             event.preventDefault();
39695             // this is very hacky as keydown always get's upper case.
39696             
39697             var cc = String.fromCharCode(event.getCharCode());
39698             
39699             
39700             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39701             
39702         }
39703         
39704         
39705     }
39706 });/*
39707  * Based on:
39708  * Ext JS Library 1.1.1
39709  * Copyright(c) 2006-2007, Ext JS, LLC.
39710  *
39711  * Originally Released Under LGPL - original licence link has changed is not relivant.
39712  *
39713  * Fork - LGPL
39714  * <script type="text/javascript">
39715  */
39716  
39717 /**
39718  * @class Roo.form.Hidden
39719  * @extends Roo.form.TextField
39720  * Simple Hidden element used on forms 
39721  * 
39722  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39723  * 
39724  * @constructor
39725  * Creates a new Hidden form element.
39726  * @param {Object} config Configuration options
39727  */
39728
39729
39730
39731 // easy hidden field...
39732 Roo.form.Hidden = function(config){
39733     Roo.form.Hidden.superclass.constructor.call(this, config);
39734 };
39735   
39736 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39737     fieldLabel:      '',
39738     inputType:      'hidden',
39739     width:          50,
39740     allowBlank:     true,
39741     labelSeparator: '',
39742     hidden:         true,
39743     itemCls :       'x-form-item-display-none'
39744
39745
39746 });
39747
39748
39749 /*
39750  * Based on:
39751  * Ext JS Library 1.1.1
39752  * Copyright(c) 2006-2007, Ext JS, LLC.
39753  *
39754  * Originally Released Under LGPL - original licence link has changed is not relivant.
39755  *
39756  * Fork - LGPL
39757  * <script type="text/javascript">
39758  */
39759  
39760 /**
39761  * @class Roo.form.TriggerField
39762  * @extends Roo.form.TextField
39763  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39764  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39765  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39766  * for which you can provide a custom implementation.  For example:
39767  * <pre><code>
39768 var trigger = new Roo.form.TriggerField();
39769 trigger.onTriggerClick = myTriggerFn;
39770 trigger.applyTo('my-field');
39771 </code></pre>
39772  *
39773  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39774  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39775  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39776  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39777  * @constructor
39778  * Create a new TriggerField.
39779  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39780  * to the base TextField)
39781  */
39782 Roo.form.TriggerField = function(config){
39783     this.mimicing = false;
39784     Roo.form.TriggerField.superclass.constructor.call(this, config);
39785 };
39786
39787 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39788     /**
39789      * @cfg {String} triggerClass A CSS class to apply to the trigger
39790      */
39791     /**
39792      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39793      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39794      */
39795     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39796     /**
39797      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39798      */
39799     hideTrigger:false,
39800
39801     /** @cfg {Boolean} grow @hide */
39802     /** @cfg {Number} growMin @hide */
39803     /** @cfg {Number} growMax @hide */
39804
39805     /**
39806      * @hide 
39807      * @method
39808      */
39809     autoSize: Roo.emptyFn,
39810     // private
39811     monitorTab : true,
39812     // private
39813     deferHeight : true,
39814
39815     
39816     actionMode : 'wrap',
39817     // private
39818     onResize : function(w, h){
39819         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39820         if(typeof w == 'number'){
39821             var x = w - this.trigger.getWidth();
39822             this.el.setWidth(this.adjustWidth('input', x));
39823             this.trigger.setStyle('left', x+'px');
39824         }
39825     },
39826
39827     // private
39828     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39829
39830     // private
39831     getResizeEl : function(){
39832         return this.wrap;
39833     },
39834
39835     // private
39836     getPositionEl : function(){
39837         return this.wrap;
39838     },
39839
39840     // private
39841     alignErrorIcon : function(){
39842         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39843     },
39844
39845     // private
39846     onRender : function(ct, position){
39847         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39848         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39849         this.trigger = this.wrap.createChild(this.triggerConfig ||
39850                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39851         if(this.hideTrigger){
39852             this.trigger.setDisplayed(false);
39853         }
39854         this.initTrigger();
39855         if(!this.width){
39856             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39857         }
39858     },
39859
39860     // private
39861     initTrigger : function(){
39862         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39863         this.trigger.addClassOnOver('x-form-trigger-over');
39864         this.trigger.addClassOnClick('x-form-trigger-click');
39865     },
39866
39867     // private
39868     onDestroy : function(){
39869         if(this.trigger){
39870             this.trigger.removeAllListeners();
39871             this.trigger.remove();
39872         }
39873         if(this.wrap){
39874             this.wrap.remove();
39875         }
39876         Roo.form.TriggerField.superclass.onDestroy.call(this);
39877     },
39878
39879     // private
39880     onFocus : function(){
39881         Roo.form.TriggerField.superclass.onFocus.call(this);
39882         if(!this.mimicing){
39883             this.wrap.addClass('x-trigger-wrap-focus');
39884             this.mimicing = true;
39885             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39886             if(this.monitorTab){
39887                 this.el.on("keydown", this.checkTab, this);
39888             }
39889         }
39890     },
39891
39892     // private
39893     checkTab : function(e){
39894         if(e.getKey() == e.TAB){
39895             this.triggerBlur();
39896         }
39897     },
39898
39899     // private
39900     onBlur : function(){
39901         // do nothing
39902     },
39903
39904     // private
39905     mimicBlur : function(e, t){
39906         if(!this.wrap.contains(t) && this.validateBlur()){
39907             this.triggerBlur();
39908         }
39909     },
39910
39911     // private
39912     triggerBlur : function(){
39913         this.mimicing = false;
39914         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39915         if(this.monitorTab){
39916             this.el.un("keydown", this.checkTab, this);
39917         }
39918         this.wrap.removeClass('x-trigger-wrap-focus');
39919         Roo.form.TriggerField.superclass.onBlur.call(this);
39920     },
39921
39922     // private
39923     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39924     validateBlur : function(e, t){
39925         return true;
39926     },
39927
39928     // private
39929     onDisable : function(){
39930         Roo.form.TriggerField.superclass.onDisable.call(this);
39931         if(this.wrap){
39932             this.wrap.addClass('x-item-disabled');
39933         }
39934     },
39935
39936     // private
39937     onEnable : function(){
39938         Roo.form.TriggerField.superclass.onEnable.call(this);
39939         if(this.wrap){
39940             this.wrap.removeClass('x-item-disabled');
39941         }
39942     },
39943
39944     // private
39945     onShow : function(){
39946         var ae = this.getActionEl();
39947         
39948         if(ae){
39949             ae.dom.style.display = '';
39950             ae.dom.style.visibility = 'visible';
39951         }
39952     },
39953
39954     // private
39955     
39956     onHide : function(){
39957         var ae = this.getActionEl();
39958         ae.dom.style.display = 'none';
39959     },
39960
39961     /**
39962      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39963      * by an implementing function.
39964      * @method
39965      * @param {EventObject} e
39966      */
39967     onTriggerClick : Roo.emptyFn
39968 });
39969
39970 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39971 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39972 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39973 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39974     initComponent : function(){
39975         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39976
39977         this.triggerConfig = {
39978             tag:'span', cls:'x-form-twin-triggers', cn:[
39979             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39980             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39981         ]};
39982     },
39983
39984     getTrigger : function(index){
39985         return this.triggers[index];
39986     },
39987
39988     initTrigger : function(){
39989         var ts = this.trigger.select('.x-form-trigger', true);
39990         this.wrap.setStyle('overflow', 'hidden');
39991         var triggerField = this;
39992         ts.each(function(t, all, index){
39993             t.hide = function(){
39994                 var w = triggerField.wrap.getWidth();
39995                 this.dom.style.display = 'none';
39996                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39997             };
39998             t.show = function(){
39999                 var w = triggerField.wrap.getWidth();
40000                 this.dom.style.display = '';
40001                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40002             };
40003             var triggerIndex = 'Trigger'+(index+1);
40004
40005             if(this['hide'+triggerIndex]){
40006                 t.dom.style.display = 'none';
40007             }
40008             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40009             t.addClassOnOver('x-form-trigger-over');
40010             t.addClassOnClick('x-form-trigger-click');
40011         }, this);
40012         this.triggers = ts.elements;
40013     },
40014
40015     onTrigger1Click : Roo.emptyFn,
40016     onTrigger2Click : Roo.emptyFn
40017 });/*
40018  * Based on:
40019  * Ext JS Library 1.1.1
40020  * Copyright(c) 2006-2007, Ext JS, LLC.
40021  *
40022  * Originally Released Under LGPL - original licence link has changed is not relivant.
40023  *
40024  * Fork - LGPL
40025  * <script type="text/javascript">
40026  */
40027  
40028 /**
40029  * @class Roo.form.TextArea
40030  * @extends Roo.form.TextField
40031  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40032  * support for auto-sizing.
40033  * @constructor
40034  * Creates a new TextArea
40035  * @param {Object} config Configuration options
40036  */
40037 Roo.form.TextArea = function(config){
40038     Roo.form.TextArea.superclass.constructor.call(this, config);
40039     // these are provided exchanges for backwards compat
40040     // minHeight/maxHeight were replaced by growMin/growMax to be
40041     // compatible with TextField growing config values
40042     if(this.minHeight !== undefined){
40043         this.growMin = this.minHeight;
40044     }
40045     if(this.maxHeight !== undefined){
40046         this.growMax = this.maxHeight;
40047     }
40048 };
40049
40050 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40051     /**
40052      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40053      */
40054     growMin : 60,
40055     /**
40056      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40057      */
40058     growMax: 1000,
40059     /**
40060      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40061      * in the field (equivalent to setting overflow: hidden, defaults to false)
40062      */
40063     preventScrollbars: false,
40064     /**
40065      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40066      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40067      */
40068
40069     // private
40070     onRender : function(ct, position){
40071         if(!this.el){
40072             this.defaultAutoCreate = {
40073                 tag: "textarea",
40074                 style:"width:300px;height:60px;",
40075                 autocomplete: "new-password"
40076             };
40077         }
40078         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40079         if(this.grow){
40080             this.textSizeEl = Roo.DomHelper.append(document.body, {
40081                 tag: "pre", cls: "x-form-grow-sizer"
40082             });
40083             if(this.preventScrollbars){
40084                 this.el.setStyle("overflow", "hidden");
40085             }
40086             this.el.setHeight(this.growMin);
40087         }
40088     },
40089
40090     onDestroy : function(){
40091         if(this.textSizeEl){
40092             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40093         }
40094         Roo.form.TextArea.superclass.onDestroy.call(this);
40095     },
40096
40097     // private
40098     onKeyUp : function(e){
40099         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40100             this.autoSize();
40101         }
40102     },
40103
40104     /**
40105      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40106      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40107      */
40108     autoSize : function(){
40109         if(!this.grow || !this.textSizeEl){
40110             return;
40111         }
40112         var el = this.el;
40113         var v = el.dom.value;
40114         var ts = this.textSizeEl;
40115
40116         ts.innerHTML = '';
40117         ts.appendChild(document.createTextNode(v));
40118         v = ts.innerHTML;
40119
40120         Roo.fly(ts).setWidth(this.el.getWidth());
40121         if(v.length < 1){
40122             v = "&#160;&#160;";
40123         }else{
40124             if(Roo.isIE){
40125                 v = v.replace(/\n/g, '<p>&#160;</p>');
40126             }
40127             v += "&#160;\n&#160;";
40128         }
40129         ts.innerHTML = v;
40130         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40131         if(h != this.lastHeight){
40132             this.lastHeight = h;
40133             this.el.setHeight(h);
40134             this.fireEvent("autosize", this, h);
40135         }
40136     }
40137 });/*
40138  * Based on:
40139  * Ext JS Library 1.1.1
40140  * Copyright(c) 2006-2007, Ext JS, LLC.
40141  *
40142  * Originally Released Under LGPL - original licence link has changed is not relivant.
40143  *
40144  * Fork - LGPL
40145  * <script type="text/javascript">
40146  */
40147  
40148
40149 /**
40150  * @class Roo.form.NumberField
40151  * @extends Roo.form.TextField
40152  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40153  * @constructor
40154  * Creates a new NumberField
40155  * @param {Object} config Configuration options
40156  */
40157 Roo.form.NumberField = function(config){
40158     Roo.form.NumberField.superclass.constructor.call(this, config);
40159 };
40160
40161 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40162     /**
40163      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40164      */
40165     fieldClass: "x-form-field x-form-num-field",
40166     /**
40167      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40168      */
40169     allowDecimals : true,
40170     /**
40171      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40172      */
40173     decimalSeparator : ".",
40174     /**
40175      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40176      */
40177     decimalPrecision : 2,
40178     /**
40179      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40180      */
40181     allowNegative : true,
40182     /**
40183      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40184      */
40185     minValue : Number.NEGATIVE_INFINITY,
40186     /**
40187      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40188      */
40189     maxValue : Number.MAX_VALUE,
40190     /**
40191      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40192      */
40193     minText : "The minimum value for this field is {0}",
40194     /**
40195      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40196      */
40197     maxText : "The maximum value for this field is {0}",
40198     /**
40199      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40200      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40201      */
40202     nanText : "{0} is not a valid number",
40203
40204     // private
40205     initEvents : function(){
40206         Roo.form.NumberField.superclass.initEvents.call(this);
40207         var allowed = "0123456789";
40208         if(this.allowDecimals){
40209             allowed += this.decimalSeparator;
40210         }
40211         if(this.allowNegative){
40212             allowed += "-";
40213         }
40214         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40215         var keyPress = function(e){
40216             var k = e.getKey();
40217             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40218                 return;
40219             }
40220             var c = e.getCharCode();
40221             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40222                 e.stopEvent();
40223             }
40224         };
40225         this.el.on("keypress", keyPress, this);
40226     },
40227
40228     // private
40229     validateValue : function(value){
40230         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40231             return false;
40232         }
40233         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40234              return true;
40235         }
40236         var num = this.parseValue(value);
40237         if(isNaN(num)){
40238             this.markInvalid(String.format(this.nanText, value));
40239             return false;
40240         }
40241         if(num < this.minValue){
40242             this.markInvalid(String.format(this.minText, this.minValue));
40243             return false;
40244         }
40245         if(num > this.maxValue){
40246             this.markInvalid(String.format(this.maxText, this.maxValue));
40247             return false;
40248         }
40249         return true;
40250     },
40251
40252     getValue : function(){
40253         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40254     },
40255
40256     // private
40257     parseValue : function(value){
40258         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40259         return isNaN(value) ? '' : value;
40260     },
40261
40262     // private
40263     fixPrecision : function(value){
40264         var nan = isNaN(value);
40265         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40266             return nan ? '' : value;
40267         }
40268         return parseFloat(value).toFixed(this.decimalPrecision);
40269     },
40270
40271     setValue : function(v){
40272         v = this.fixPrecision(v);
40273         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40274     },
40275
40276     // private
40277     decimalPrecisionFcn : function(v){
40278         return Math.floor(v);
40279     },
40280
40281     beforeBlur : function(){
40282         var v = this.parseValue(this.getRawValue());
40283         if(v){
40284             this.setValue(v);
40285         }
40286     }
40287 });/*
40288  * Based on:
40289  * Ext JS Library 1.1.1
40290  * Copyright(c) 2006-2007, Ext JS, LLC.
40291  *
40292  * Originally Released Under LGPL - original licence link has changed is not relivant.
40293  *
40294  * Fork - LGPL
40295  * <script type="text/javascript">
40296  */
40297  
40298 /**
40299  * @class Roo.form.DateField
40300  * @extends Roo.form.TriggerField
40301  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40302 * @constructor
40303 * Create a new DateField
40304 * @param {Object} config
40305  */
40306 Roo.form.DateField = function(config)
40307 {
40308     Roo.form.DateField.superclass.constructor.call(this, config);
40309     
40310       this.addEvents({
40311          
40312         /**
40313          * @event select
40314          * Fires when a date is selected
40315              * @param {Roo.form.DateField} combo This combo box
40316              * @param {Date} date The date selected
40317              */
40318         'select' : true
40319          
40320     });
40321     
40322     
40323     if(typeof this.minValue == "string") {
40324         this.minValue = this.parseDate(this.minValue);
40325     }
40326     if(typeof this.maxValue == "string") {
40327         this.maxValue = this.parseDate(this.maxValue);
40328     }
40329     this.ddMatch = null;
40330     if(this.disabledDates){
40331         var dd = this.disabledDates;
40332         var re = "(?:";
40333         for(var i = 0; i < dd.length; i++){
40334             re += dd[i];
40335             if(i != dd.length-1) {
40336                 re += "|";
40337             }
40338         }
40339         this.ddMatch = new RegExp(re + ")");
40340     }
40341 };
40342
40343 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40344     /**
40345      * @cfg {String} format
40346      * The default date format string which can be overriden for localization support.  The format must be
40347      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40348      */
40349     format : "m/d/y",
40350     /**
40351      * @cfg {String} altFormats
40352      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40353      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40354      */
40355     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40356     /**
40357      * @cfg {Array} disabledDays
40358      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40359      */
40360     disabledDays : null,
40361     /**
40362      * @cfg {String} disabledDaysText
40363      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40364      */
40365     disabledDaysText : "Disabled",
40366     /**
40367      * @cfg {Array} disabledDates
40368      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40369      * expression so they are very powerful. Some examples:
40370      * <ul>
40371      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40372      * <li>["03/08", "09/16"] would disable those days for every year</li>
40373      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40374      * <li>["03/../2006"] would disable every day in March 2006</li>
40375      * <li>["^03"] would disable every day in every March</li>
40376      * </ul>
40377      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40378      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40379      */
40380     disabledDates : null,
40381     /**
40382      * @cfg {String} disabledDatesText
40383      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40384      */
40385     disabledDatesText : "Disabled",
40386     /**
40387      * @cfg {Date/String} minValue
40388      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40389      * valid format (defaults to null).
40390      */
40391     minValue : null,
40392     /**
40393      * @cfg {Date/String} maxValue
40394      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40395      * valid format (defaults to null).
40396      */
40397     maxValue : null,
40398     /**
40399      * @cfg {String} minText
40400      * The error text to display when the date in the cell is before minValue (defaults to
40401      * 'The date in this field must be after {minValue}').
40402      */
40403     minText : "The date in this field must be equal to or after {0}",
40404     /**
40405      * @cfg {String} maxText
40406      * The error text to display when the date in the cell is after maxValue (defaults to
40407      * 'The date in this field must be before {maxValue}').
40408      */
40409     maxText : "The date in this field must be equal to or before {0}",
40410     /**
40411      * @cfg {String} invalidText
40412      * The error text to display when the date in the field is invalid (defaults to
40413      * '{value} is not a valid date - it must be in the format {format}').
40414      */
40415     invalidText : "{0} is not a valid date - it must be in the format {1}",
40416     /**
40417      * @cfg {String} triggerClass
40418      * An additional CSS class used to style the trigger button.  The trigger will always get the
40419      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40420      * which displays a calendar icon).
40421      */
40422     triggerClass : 'x-form-date-trigger',
40423     
40424
40425     /**
40426      * @cfg {Boolean} useIso
40427      * if enabled, then the date field will use a hidden field to store the 
40428      * real value as iso formated date. default (false)
40429      */ 
40430     useIso : false,
40431     /**
40432      * @cfg {String/Object} autoCreate
40433      * A DomHelper element spec, or true for a default element spec (defaults to
40434      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40435      */ 
40436     // private
40437     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40438     
40439     // private
40440     hiddenField: false,
40441     
40442     onRender : function(ct, position)
40443     {
40444         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40445         if (this.useIso) {
40446             //this.el.dom.removeAttribute('name'); 
40447             Roo.log("Changing name?");
40448             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40449             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40450                     'before', true);
40451             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40452             // prevent input submission
40453             this.hiddenName = this.name;
40454         }
40455             
40456             
40457     },
40458     
40459     // private
40460     validateValue : function(value)
40461     {
40462         value = this.formatDate(value);
40463         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40464             Roo.log('super failed');
40465             return false;
40466         }
40467         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40468              return true;
40469         }
40470         var svalue = value;
40471         value = this.parseDate(value);
40472         if(!value){
40473             Roo.log('parse date failed' + svalue);
40474             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40475             return false;
40476         }
40477         var time = value.getTime();
40478         if(this.minValue && time < this.minValue.getTime()){
40479             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40480             return false;
40481         }
40482         if(this.maxValue && time > this.maxValue.getTime()){
40483             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40484             return false;
40485         }
40486         if(this.disabledDays){
40487             var day = value.getDay();
40488             for(var i = 0; i < this.disabledDays.length; i++) {
40489                 if(day === this.disabledDays[i]){
40490                     this.markInvalid(this.disabledDaysText);
40491                     return false;
40492                 }
40493             }
40494         }
40495         var fvalue = this.formatDate(value);
40496         if(this.ddMatch && this.ddMatch.test(fvalue)){
40497             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40498             return false;
40499         }
40500         return true;
40501     },
40502
40503     // private
40504     // Provides logic to override the default TriggerField.validateBlur which just returns true
40505     validateBlur : function(){
40506         return !this.menu || !this.menu.isVisible();
40507     },
40508     
40509     getName: function()
40510     {
40511         // returns hidden if it's set..
40512         if (!this.rendered) {return ''};
40513         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40514         
40515     },
40516
40517     /**
40518      * Returns the current date value of the date field.
40519      * @return {Date} The date value
40520      */
40521     getValue : function(){
40522         
40523         return  this.hiddenField ?
40524                 this.hiddenField.value :
40525                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40526     },
40527
40528     /**
40529      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40530      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40531      * (the default format used is "m/d/y").
40532      * <br />Usage:
40533      * <pre><code>
40534 //All of these calls set the same date value (May 4, 2006)
40535
40536 //Pass a date object:
40537 var dt = new Date('5/4/06');
40538 dateField.setValue(dt);
40539
40540 //Pass a date string (default format):
40541 dateField.setValue('5/4/06');
40542
40543 //Pass a date string (custom format):
40544 dateField.format = 'Y-m-d';
40545 dateField.setValue('2006-5-4');
40546 </code></pre>
40547      * @param {String/Date} date The date or valid date string
40548      */
40549     setValue : function(date){
40550         if (this.hiddenField) {
40551             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40552         }
40553         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40554         // make sure the value field is always stored as a date..
40555         this.value = this.parseDate(date);
40556         
40557         
40558     },
40559
40560     // private
40561     parseDate : function(value){
40562         if(!value || value instanceof Date){
40563             return value;
40564         }
40565         var v = Date.parseDate(value, this.format);
40566          if (!v && this.useIso) {
40567             v = Date.parseDate(value, 'Y-m-d');
40568         }
40569         if(!v && this.altFormats){
40570             if(!this.altFormatsArray){
40571                 this.altFormatsArray = this.altFormats.split("|");
40572             }
40573             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40574                 v = Date.parseDate(value, this.altFormatsArray[i]);
40575             }
40576         }
40577         return v;
40578     },
40579
40580     // private
40581     formatDate : function(date, fmt){
40582         return (!date || !(date instanceof Date)) ?
40583                date : date.dateFormat(fmt || this.format);
40584     },
40585
40586     // private
40587     menuListeners : {
40588         select: function(m, d){
40589             
40590             this.setValue(d);
40591             this.fireEvent('select', this, d);
40592         },
40593         show : function(){ // retain focus styling
40594             this.onFocus();
40595         },
40596         hide : function(){
40597             this.focus.defer(10, this);
40598             var ml = this.menuListeners;
40599             this.menu.un("select", ml.select,  this);
40600             this.menu.un("show", ml.show,  this);
40601             this.menu.un("hide", ml.hide,  this);
40602         }
40603     },
40604
40605     // private
40606     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40607     onTriggerClick : function(){
40608         if(this.disabled){
40609             return;
40610         }
40611         if(this.menu == null){
40612             this.menu = new Roo.menu.DateMenu();
40613         }
40614         Roo.apply(this.menu.picker,  {
40615             showClear: this.allowBlank,
40616             minDate : this.minValue,
40617             maxDate : this.maxValue,
40618             disabledDatesRE : this.ddMatch,
40619             disabledDatesText : this.disabledDatesText,
40620             disabledDays : this.disabledDays,
40621             disabledDaysText : this.disabledDaysText,
40622             format : this.useIso ? 'Y-m-d' : this.format,
40623             minText : String.format(this.minText, this.formatDate(this.minValue)),
40624             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40625         });
40626         this.menu.on(Roo.apply({}, this.menuListeners, {
40627             scope:this
40628         }));
40629         this.menu.picker.setValue(this.getValue() || new Date());
40630         this.menu.show(this.el, "tl-bl?");
40631     },
40632
40633     beforeBlur : function(){
40634         var v = this.parseDate(this.getRawValue());
40635         if(v){
40636             this.setValue(v);
40637         }
40638     },
40639
40640     /*@
40641      * overide
40642      * 
40643      */
40644     isDirty : function() {
40645         if(this.disabled) {
40646             return false;
40647         }
40648         
40649         if(typeof(this.startValue) === 'undefined'){
40650             return false;
40651         }
40652         
40653         return String(this.getValue()) !== String(this.startValue);
40654         
40655     },
40656     // @overide
40657     cleanLeadingSpace : function(e)
40658     {
40659        return;
40660     }
40661     
40662 });/*
40663  * Based on:
40664  * Ext JS Library 1.1.1
40665  * Copyright(c) 2006-2007, Ext JS, LLC.
40666  *
40667  * Originally Released Under LGPL - original licence link has changed is not relivant.
40668  *
40669  * Fork - LGPL
40670  * <script type="text/javascript">
40671  */
40672  
40673 /**
40674  * @class Roo.form.MonthField
40675  * @extends Roo.form.TriggerField
40676  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40677 * @constructor
40678 * Create a new MonthField
40679 * @param {Object} config
40680  */
40681 Roo.form.MonthField = function(config){
40682     
40683     Roo.form.MonthField.superclass.constructor.call(this, config);
40684     
40685       this.addEvents({
40686          
40687         /**
40688          * @event select
40689          * Fires when a date is selected
40690              * @param {Roo.form.MonthFieeld} combo This combo box
40691              * @param {Date} date The date selected
40692              */
40693         'select' : true
40694          
40695     });
40696     
40697     
40698     if(typeof this.minValue == "string") {
40699         this.minValue = this.parseDate(this.minValue);
40700     }
40701     if(typeof this.maxValue == "string") {
40702         this.maxValue = this.parseDate(this.maxValue);
40703     }
40704     this.ddMatch = null;
40705     if(this.disabledDates){
40706         var dd = this.disabledDates;
40707         var re = "(?:";
40708         for(var i = 0; i < dd.length; i++){
40709             re += dd[i];
40710             if(i != dd.length-1) {
40711                 re += "|";
40712             }
40713         }
40714         this.ddMatch = new RegExp(re + ")");
40715     }
40716 };
40717
40718 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40719     /**
40720      * @cfg {String} format
40721      * The default date format string which can be overriden for localization support.  The format must be
40722      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40723      */
40724     format : "M Y",
40725     /**
40726      * @cfg {String} altFormats
40727      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40728      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40729      */
40730     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40731     /**
40732      * @cfg {Array} disabledDays
40733      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40734      */
40735     disabledDays : [0,1,2,3,4,5,6],
40736     /**
40737      * @cfg {String} disabledDaysText
40738      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40739      */
40740     disabledDaysText : "Disabled",
40741     /**
40742      * @cfg {Array} disabledDates
40743      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40744      * expression so they are very powerful. Some examples:
40745      * <ul>
40746      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40747      * <li>["03/08", "09/16"] would disable those days for every year</li>
40748      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40749      * <li>["03/../2006"] would disable every day in March 2006</li>
40750      * <li>["^03"] would disable every day in every March</li>
40751      * </ul>
40752      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40753      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40754      */
40755     disabledDates : null,
40756     /**
40757      * @cfg {String} disabledDatesText
40758      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40759      */
40760     disabledDatesText : "Disabled",
40761     /**
40762      * @cfg {Date/String} minValue
40763      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40764      * valid format (defaults to null).
40765      */
40766     minValue : null,
40767     /**
40768      * @cfg {Date/String} maxValue
40769      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40770      * valid format (defaults to null).
40771      */
40772     maxValue : null,
40773     /**
40774      * @cfg {String} minText
40775      * The error text to display when the date in the cell is before minValue (defaults to
40776      * 'The date in this field must be after {minValue}').
40777      */
40778     minText : "The date in this field must be equal to or after {0}",
40779     /**
40780      * @cfg {String} maxTextf
40781      * The error text to display when the date in the cell is after maxValue (defaults to
40782      * 'The date in this field must be before {maxValue}').
40783      */
40784     maxText : "The date in this field must be equal to or before {0}",
40785     /**
40786      * @cfg {String} invalidText
40787      * The error text to display when the date in the field is invalid (defaults to
40788      * '{value} is not a valid date - it must be in the format {format}').
40789      */
40790     invalidText : "{0} is not a valid date - it must be in the format {1}",
40791     /**
40792      * @cfg {String} triggerClass
40793      * An additional CSS class used to style the trigger button.  The trigger will always get the
40794      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40795      * which displays a calendar icon).
40796      */
40797     triggerClass : 'x-form-date-trigger',
40798     
40799
40800     /**
40801      * @cfg {Boolean} useIso
40802      * if enabled, then the date field will use a hidden field to store the 
40803      * real value as iso formated date. default (true)
40804      */ 
40805     useIso : true,
40806     /**
40807      * @cfg {String/Object} autoCreate
40808      * A DomHelper element spec, or true for a default element spec (defaults to
40809      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40810      */ 
40811     // private
40812     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40813     
40814     // private
40815     hiddenField: false,
40816     
40817     hideMonthPicker : false,
40818     
40819     onRender : function(ct, position)
40820     {
40821         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40822         if (this.useIso) {
40823             this.el.dom.removeAttribute('name'); 
40824             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40825                     'before', true);
40826             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40827             // prevent input submission
40828             this.hiddenName = this.name;
40829         }
40830             
40831             
40832     },
40833     
40834     // private
40835     validateValue : function(value)
40836     {
40837         value = this.formatDate(value);
40838         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40839             return false;
40840         }
40841         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40842              return true;
40843         }
40844         var svalue = value;
40845         value = this.parseDate(value);
40846         if(!value){
40847             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40848             return false;
40849         }
40850         var time = value.getTime();
40851         if(this.minValue && time < this.minValue.getTime()){
40852             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40853             return false;
40854         }
40855         if(this.maxValue && time > this.maxValue.getTime()){
40856             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40857             return false;
40858         }
40859         /*if(this.disabledDays){
40860             var day = value.getDay();
40861             for(var i = 0; i < this.disabledDays.length; i++) {
40862                 if(day === this.disabledDays[i]){
40863                     this.markInvalid(this.disabledDaysText);
40864                     return false;
40865                 }
40866             }
40867         }
40868         */
40869         var fvalue = this.formatDate(value);
40870         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40871             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40872             return false;
40873         }
40874         */
40875         return true;
40876     },
40877
40878     // private
40879     // Provides logic to override the default TriggerField.validateBlur which just returns true
40880     validateBlur : function(){
40881         return !this.menu || !this.menu.isVisible();
40882     },
40883
40884     /**
40885      * Returns the current date value of the date field.
40886      * @return {Date} The date value
40887      */
40888     getValue : function(){
40889         
40890         
40891         
40892         return  this.hiddenField ?
40893                 this.hiddenField.value :
40894                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40895     },
40896
40897     /**
40898      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40899      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40900      * (the default format used is "m/d/y").
40901      * <br />Usage:
40902      * <pre><code>
40903 //All of these calls set the same date value (May 4, 2006)
40904
40905 //Pass a date object:
40906 var dt = new Date('5/4/06');
40907 monthField.setValue(dt);
40908
40909 //Pass a date string (default format):
40910 monthField.setValue('5/4/06');
40911
40912 //Pass a date string (custom format):
40913 monthField.format = 'Y-m-d';
40914 monthField.setValue('2006-5-4');
40915 </code></pre>
40916      * @param {String/Date} date The date or valid date string
40917      */
40918     setValue : function(date){
40919         Roo.log('month setValue' + date);
40920         // can only be first of month..
40921         
40922         var val = this.parseDate(date);
40923         
40924         if (this.hiddenField) {
40925             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40926         }
40927         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40928         this.value = this.parseDate(date);
40929     },
40930
40931     // private
40932     parseDate : function(value){
40933         if(!value || value instanceof Date){
40934             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40935             return value;
40936         }
40937         var v = Date.parseDate(value, this.format);
40938         if (!v && this.useIso) {
40939             v = Date.parseDate(value, 'Y-m-d');
40940         }
40941         if (v) {
40942             // 
40943             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40944         }
40945         
40946         
40947         if(!v && this.altFormats){
40948             if(!this.altFormatsArray){
40949                 this.altFormatsArray = this.altFormats.split("|");
40950             }
40951             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40952                 v = Date.parseDate(value, this.altFormatsArray[i]);
40953             }
40954         }
40955         return v;
40956     },
40957
40958     // private
40959     formatDate : function(date, fmt){
40960         return (!date || !(date instanceof Date)) ?
40961                date : date.dateFormat(fmt || this.format);
40962     },
40963
40964     // private
40965     menuListeners : {
40966         select: function(m, d){
40967             this.setValue(d);
40968             this.fireEvent('select', this, d);
40969         },
40970         show : function(){ // retain focus styling
40971             this.onFocus();
40972         },
40973         hide : function(){
40974             this.focus.defer(10, this);
40975             var ml = this.menuListeners;
40976             this.menu.un("select", ml.select,  this);
40977             this.menu.un("show", ml.show,  this);
40978             this.menu.un("hide", ml.hide,  this);
40979         }
40980     },
40981     // private
40982     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40983     onTriggerClick : function(){
40984         if(this.disabled){
40985             return;
40986         }
40987         if(this.menu == null){
40988             this.menu = new Roo.menu.DateMenu();
40989            
40990         }
40991         
40992         Roo.apply(this.menu.picker,  {
40993             
40994             showClear: this.allowBlank,
40995             minDate : this.minValue,
40996             maxDate : this.maxValue,
40997             disabledDatesRE : this.ddMatch,
40998             disabledDatesText : this.disabledDatesText,
40999             
41000             format : this.useIso ? 'Y-m-d' : this.format,
41001             minText : String.format(this.minText, this.formatDate(this.minValue)),
41002             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41003             
41004         });
41005          this.menu.on(Roo.apply({}, this.menuListeners, {
41006             scope:this
41007         }));
41008        
41009         
41010         var m = this.menu;
41011         var p = m.picker;
41012         
41013         // hide month picker get's called when we called by 'before hide';
41014         
41015         var ignorehide = true;
41016         p.hideMonthPicker  = function(disableAnim){
41017             if (ignorehide) {
41018                 return;
41019             }
41020              if(this.monthPicker){
41021                 Roo.log("hideMonthPicker called");
41022                 if(disableAnim === true){
41023                     this.monthPicker.hide();
41024                 }else{
41025                     this.monthPicker.slideOut('t', {duration:.2});
41026                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41027                     p.fireEvent("select", this, this.value);
41028                     m.hide();
41029                 }
41030             }
41031         }
41032         
41033         Roo.log('picker set value');
41034         Roo.log(this.getValue());
41035         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41036         m.show(this.el, 'tl-bl?');
41037         ignorehide  = false;
41038         // this will trigger hideMonthPicker..
41039         
41040         
41041         // hidden the day picker
41042         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41043         
41044         
41045         
41046       
41047         
41048         p.showMonthPicker.defer(100, p);
41049     
41050         
41051        
41052     },
41053
41054     beforeBlur : function(){
41055         var v = this.parseDate(this.getRawValue());
41056         if(v){
41057             this.setValue(v);
41058         }
41059     }
41060
41061     /** @cfg {Boolean} grow @hide */
41062     /** @cfg {Number} growMin @hide */
41063     /** @cfg {Number} growMax @hide */
41064     /**
41065      * @hide
41066      * @method autoSize
41067      */
41068 });/*
41069  * Based on:
41070  * Ext JS Library 1.1.1
41071  * Copyright(c) 2006-2007, Ext JS, LLC.
41072  *
41073  * Originally Released Under LGPL - original licence link has changed is not relivant.
41074  *
41075  * Fork - LGPL
41076  * <script type="text/javascript">
41077  */
41078  
41079
41080 /**
41081  * @class Roo.form.ComboBox
41082  * @extends Roo.form.TriggerField
41083  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41084  * @constructor
41085  * Create a new ComboBox.
41086  * @param {Object} config Configuration options
41087  */
41088 Roo.form.ComboBox = function(config){
41089     Roo.form.ComboBox.superclass.constructor.call(this, config);
41090     this.addEvents({
41091         /**
41092          * @event expand
41093          * Fires when the dropdown list is expanded
41094              * @param {Roo.form.ComboBox} combo This combo box
41095              */
41096         'expand' : true,
41097         /**
41098          * @event collapse
41099          * Fires when the dropdown list is collapsed
41100              * @param {Roo.form.ComboBox} combo This combo box
41101              */
41102         'collapse' : true,
41103         /**
41104          * @event beforeselect
41105          * Fires before a list item is selected. Return false to cancel the selection.
41106              * @param {Roo.form.ComboBox} combo This combo box
41107              * @param {Roo.data.Record} record The data record returned from the underlying store
41108              * @param {Number} index The index of the selected item in the dropdown list
41109              */
41110         'beforeselect' : true,
41111         /**
41112          * @event select
41113          * Fires when a list item is selected
41114              * @param {Roo.form.ComboBox} combo This combo box
41115              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41116              * @param {Number} index The index of the selected item in the dropdown list
41117              */
41118         'select' : true,
41119         /**
41120          * @event beforequery
41121          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41122          * The event object passed has these properties:
41123              * @param {Roo.form.ComboBox} combo This combo box
41124              * @param {String} query The query
41125              * @param {Boolean} forceAll true to force "all" query
41126              * @param {Boolean} cancel true to cancel the query
41127              * @param {Object} e The query event object
41128              */
41129         'beforequery': true,
41130          /**
41131          * @event add
41132          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41133              * @param {Roo.form.ComboBox} combo This combo box
41134              */
41135         'add' : true,
41136         /**
41137          * @event edit
41138          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41139              * @param {Roo.form.ComboBox} combo This combo box
41140              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41141              */
41142         'edit' : true
41143         
41144         
41145     });
41146     if(this.transform){
41147         this.allowDomMove = false;
41148         var s = Roo.getDom(this.transform);
41149         if(!this.hiddenName){
41150             this.hiddenName = s.name;
41151         }
41152         if(!this.store){
41153             this.mode = 'local';
41154             var d = [], opts = s.options;
41155             for(var i = 0, len = opts.length;i < len; i++){
41156                 var o = opts[i];
41157                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41158                 if(o.selected) {
41159                     this.value = value;
41160                 }
41161                 d.push([value, o.text]);
41162             }
41163             this.store = new Roo.data.SimpleStore({
41164                 'id': 0,
41165                 fields: ['value', 'text'],
41166                 data : d
41167             });
41168             this.valueField = 'value';
41169             this.displayField = 'text';
41170         }
41171         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41172         if(!this.lazyRender){
41173             this.target = true;
41174             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41175             s.parentNode.removeChild(s); // remove it
41176             this.render(this.el.parentNode);
41177         }else{
41178             s.parentNode.removeChild(s); // remove it
41179         }
41180
41181     }
41182     if (this.store) {
41183         this.store = Roo.factory(this.store, Roo.data);
41184     }
41185     
41186     this.selectedIndex = -1;
41187     if(this.mode == 'local'){
41188         if(config.queryDelay === undefined){
41189             this.queryDelay = 10;
41190         }
41191         if(config.minChars === undefined){
41192             this.minChars = 0;
41193         }
41194     }
41195 };
41196
41197 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41198     /**
41199      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41200      */
41201     /**
41202      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41203      * rendering into an Roo.Editor, defaults to false)
41204      */
41205     /**
41206      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41207      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41208      */
41209     /**
41210      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41211      */
41212     /**
41213      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41214      * the dropdown list (defaults to undefined, with no header element)
41215      */
41216
41217      /**
41218      * @cfg {String/Roo.Template} tpl The template to use to render the output
41219      */
41220      
41221     // private
41222     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41223     /**
41224      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41225      */
41226     listWidth: undefined,
41227     /**
41228      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41229      * mode = 'remote' or 'text' if mode = 'local')
41230      */
41231     displayField: undefined,
41232     /**
41233      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41234      * mode = 'remote' or 'value' if mode = 'local'). 
41235      * Note: use of a valueField requires the user make a selection
41236      * in order for a value to be mapped.
41237      */
41238     valueField: undefined,
41239     
41240     
41241     /**
41242      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41243      * field's data value (defaults to the underlying DOM element's name)
41244      */
41245     hiddenName: undefined,
41246     /**
41247      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41248      */
41249     listClass: '',
41250     /**
41251      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41252      */
41253     selectedClass: 'x-combo-selected',
41254     /**
41255      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41256      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41257      * which displays a downward arrow icon).
41258      */
41259     triggerClass : 'x-form-arrow-trigger',
41260     /**
41261      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41262      */
41263     shadow:'sides',
41264     /**
41265      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41266      * anchor positions (defaults to 'tl-bl')
41267      */
41268     listAlign: 'tl-bl?',
41269     /**
41270      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41271      */
41272     maxHeight: 300,
41273     /**
41274      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41275      * query specified by the allQuery config option (defaults to 'query')
41276      */
41277     triggerAction: 'query',
41278     /**
41279      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41280      * (defaults to 4, does not apply if editable = false)
41281      */
41282     minChars : 4,
41283     /**
41284      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41285      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41286      */
41287     typeAhead: false,
41288     /**
41289      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41290      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41291      */
41292     queryDelay: 500,
41293     /**
41294      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41295      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41296      */
41297     pageSize: 0,
41298     /**
41299      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41300      * when editable = true (defaults to false)
41301      */
41302     selectOnFocus:false,
41303     /**
41304      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41305      */
41306     queryParam: 'query',
41307     /**
41308      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41309      * when mode = 'remote' (defaults to 'Loading...')
41310      */
41311     loadingText: 'Loading...',
41312     /**
41313      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41314      */
41315     resizable: false,
41316     /**
41317      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41318      */
41319     handleHeight : 8,
41320     /**
41321      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41322      * traditional select (defaults to true)
41323      */
41324     editable: true,
41325     /**
41326      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41327      */
41328     allQuery: '',
41329     /**
41330      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41331      */
41332     mode: 'remote',
41333     /**
41334      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41335      * listWidth has a higher value)
41336      */
41337     minListWidth : 70,
41338     /**
41339      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41340      * allow the user to set arbitrary text into the field (defaults to false)
41341      */
41342     forceSelection:false,
41343     /**
41344      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41345      * if typeAhead = true (defaults to 250)
41346      */
41347     typeAheadDelay : 250,
41348     /**
41349      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41350      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41351      */
41352     valueNotFoundText : undefined,
41353     /**
41354      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41355      */
41356     blockFocus : false,
41357     
41358     /**
41359      * @cfg {Boolean} disableClear Disable showing of clear button.
41360      */
41361     disableClear : false,
41362     /**
41363      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41364      */
41365     alwaysQuery : false,
41366     
41367     //private
41368     addicon : false,
41369     editicon: false,
41370     
41371     // element that contains real text value.. (when hidden is used..)
41372      
41373     // private
41374     onRender : function(ct, position)
41375     {
41376         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41377         
41378         if(this.hiddenName){
41379             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41380                     'before', true);
41381             this.hiddenField.value =
41382                 this.hiddenValue !== undefined ? this.hiddenValue :
41383                 this.value !== undefined ? this.value : '';
41384
41385             // prevent input submission
41386             this.el.dom.removeAttribute('name');
41387              
41388              
41389         }
41390         
41391         if(Roo.isGecko){
41392             this.el.dom.setAttribute('autocomplete', 'off');
41393         }
41394
41395         var cls = 'x-combo-list';
41396
41397         this.list = new Roo.Layer({
41398             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41399         });
41400
41401         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41402         this.list.setWidth(lw);
41403         this.list.swallowEvent('mousewheel');
41404         this.assetHeight = 0;
41405
41406         if(this.title){
41407             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41408             this.assetHeight += this.header.getHeight();
41409         }
41410
41411         this.innerList = this.list.createChild({cls:cls+'-inner'});
41412         this.innerList.on('mouseover', this.onViewOver, this);
41413         this.innerList.on('mousemove', this.onViewMove, this);
41414         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41415         
41416         if(this.allowBlank && !this.pageSize && !this.disableClear){
41417             this.footer = this.list.createChild({cls:cls+'-ft'});
41418             this.pageTb = new Roo.Toolbar(this.footer);
41419            
41420         }
41421         if(this.pageSize){
41422             this.footer = this.list.createChild({cls:cls+'-ft'});
41423             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41424                     {pageSize: this.pageSize});
41425             
41426         }
41427         
41428         if (this.pageTb && this.allowBlank && !this.disableClear) {
41429             var _this = this;
41430             this.pageTb.add(new Roo.Toolbar.Fill(), {
41431                 cls: 'x-btn-icon x-btn-clear',
41432                 text: '&#160;',
41433                 handler: function()
41434                 {
41435                     _this.collapse();
41436                     _this.clearValue();
41437                     _this.onSelect(false, -1);
41438                 }
41439             });
41440         }
41441         if (this.footer) {
41442             this.assetHeight += this.footer.getHeight();
41443         }
41444         
41445
41446         if(!this.tpl){
41447             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41448         }
41449
41450         this.view = new Roo.View(this.innerList, this.tpl, {
41451             singleSelect:true,
41452             store: this.store,
41453             selectedClass: this.selectedClass
41454         });
41455
41456         this.view.on('click', this.onViewClick, this);
41457
41458         this.store.on('beforeload', this.onBeforeLoad, this);
41459         this.store.on('load', this.onLoad, this);
41460         this.store.on('loadexception', this.onLoadException, this);
41461
41462         if(this.resizable){
41463             this.resizer = new Roo.Resizable(this.list,  {
41464                pinned:true, handles:'se'
41465             });
41466             this.resizer.on('resize', function(r, w, h){
41467                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41468                 this.listWidth = w;
41469                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41470                 this.restrictHeight();
41471             }, this);
41472             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41473         }
41474         if(!this.editable){
41475             this.editable = true;
41476             this.setEditable(false);
41477         }  
41478         
41479         
41480         if (typeof(this.events.add.listeners) != 'undefined') {
41481             
41482             this.addicon = this.wrap.createChild(
41483                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41484        
41485             this.addicon.on('click', function(e) {
41486                 this.fireEvent('add', this);
41487             }, this);
41488         }
41489         if (typeof(this.events.edit.listeners) != 'undefined') {
41490             
41491             this.editicon = this.wrap.createChild(
41492                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41493             if (this.addicon) {
41494                 this.editicon.setStyle('margin-left', '40px');
41495             }
41496             this.editicon.on('click', function(e) {
41497                 
41498                 // we fire even  if inothing is selected..
41499                 this.fireEvent('edit', this, this.lastData );
41500                 
41501             }, this);
41502         }
41503         
41504         
41505         
41506     },
41507
41508     // private
41509     initEvents : function(){
41510         Roo.form.ComboBox.superclass.initEvents.call(this);
41511
41512         this.keyNav = new Roo.KeyNav(this.el, {
41513             "up" : function(e){
41514                 this.inKeyMode = true;
41515                 this.selectPrev();
41516             },
41517
41518             "down" : function(e){
41519                 if(!this.isExpanded()){
41520                     this.onTriggerClick();
41521                 }else{
41522                     this.inKeyMode = true;
41523                     this.selectNext();
41524                 }
41525             },
41526
41527             "enter" : function(e){
41528                 this.onViewClick();
41529                 //return true;
41530             },
41531
41532             "esc" : function(e){
41533                 this.collapse();
41534             },
41535
41536             "tab" : function(e){
41537                 this.onViewClick(false);
41538                 this.fireEvent("specialkey", this, e);
41539                 return true;
41540             },
41541
41542             scope : this,
41543
41544             doRelay : function(foo, bar, hname){
41545                 if(hname == 'down' || this.scope.isExpanded()){
41546                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41547                 }
41548                 return true;
41549             },
41550
41551             forceKeyDown: true
41552         });
41553         this.queryDelay = Math.max(this.queryDelay || 10,
41554                 this.mode == 'local' ? 10 : 250);
41555         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41556         if(this.typeAhead){
41557             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41558         }
41559         if(this.editable !== false){
41560             this.el.on("keyup", this.onKeyUp, this);
41561         }
41562         if(this.forceSelection){
41563             this.on('blur', this.doForce, this);
41564         }
41565     },
41566
41567     onDestroy : function(){
41568         if(this.view){
41569             this.view.setStore(null);
41570             this.view.el.removeAllListeners();
41571             this.view.el.remove();
41572             this.view.purgeListeners();
41573         }
41574         if(this.list){
41575             this.list.destroy();
41576         }
41577         if(this.store){
41578             this.store.un('beforeload', this.onBeforeLoad, this);
41579             this.store.un('load', this.onLoad, this);
41580             this.store.un('loadexception', this.onLoadException, this);
41581         }
41582         Roo.form.ComboBox.superclass.onDestroy.call(this);
41583     },
41584
41585     // private
41586     fireKey : function(e){
41587         if(e.isNavKeyPress() && !this.list.isVisible()){
41588             this.fireEvent("specialkey", this, e);
41589         }
41590     },
41591
41592     // private
41593     onResize: function(w, h){
41594         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41595         
41596         if(typeof w != 'number'){
41597             // we do not handle it!?!?
41598             return;
41599         }
41600         var tw = this.trigger.getWidth();
41601         tw += this.addicon ? this.addicon.getWidth() : 0;
41602         tw += this.editicon ? this.editicon.getWidth() : 0;
41603         var x = w - tw;
41604         this.el.setWidth( this.adjustWidth('input', x));
41605             
41606         this.trigger.setStyle('left', x+'px');
41607         
41608         if(this.list && this.listWidth === undefined){
41609             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41610             this.list.setWidth(lw);
41611             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41612         }
41613         
41614     
41615         
41616     },
41617
41618     /**
41619      * Allow or prevent the user from directly editing the field text.  If false is passed,
41620      * the user will only be able to select from the items defined in the dropdown list.  This method
41621      * is the runtime equivalent of setting the 'editable' config option at config time.
41622      * @param {Boolean} value True to allow the user to directly edit the field text
41623      */
41624     setEditable : function(value){
41625         if(value == this.editable){
41626             return;
41627         }
41628         this.editable = value;
41629         if(!value){
41630             this.el.dom.setAttribute('readOnly', true);
41631             this.el.on('mousedown', this.onTriggerClick,  this);
41632             this.el.addClass('x-combo-noedit');
41633         }else{
41634             this.el.dom.setAttribute('readOnly', false);
41635             this.el.un('mousedown', this.onTriggerClick,  this);
41636             this.el.removeClass('x-combo-noedit');
41637         }
41638     },
41639
41640     // private
41641     onBeforeLoad : function(){
41642         if(!this.hasFocus){
41643             return;
41644         }
41645         this.innerList.update(this.loadingText ?
41646                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41647         this.restrictHeight();
41648         this.selectedIndex = -1;
41649     },
41650
41651     // private
41652     onLoad : function(){
41653         if(!this.hasFocus){
41654             return;
41655         }
41656         if(this.store.getCount() > 0){
41657             this.expand();
41658             this.restrictHeight();
41659             if(this.lastQuery == this.allQuery){
41660                 if(this.editable){
41661                     this.el.dom.select();
41662                 }
41663                 if(!this.selectByValue(this.value, true)){
41664                     this.select(0, true);
41665                 }
41666             }else{
41667                 this.selectNext();
41668                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41669                     this.taTask.delay(this.typeAheadDelay);
41670                 }
41671             }
41672         }else{
41673             this.onEmptyResults();
41674         }
41675         //this.el.focus();
41676     },
41677     // private
41678     onLoadException : function()
41679     {
41680         this.collapse();
41681         Roo.log(this.store.reader.jsonData);
41682         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41683             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41684         }
41685         
41686         
41687     },
41688     // private
41689     onTypeAhead : function(){
41690         if(this.store.getCount() > 0){
41691             var r = this.store.getAt(0);
41692             var newValue = r.data[this.displayField];
41693             var len = newValue.length;
41694             var selStart = this.getRawValue().length;
41695             if(selStart != len){
41696                 this.setRawValue(newValue);
41697                 this.selectText(selStart, newValue.length);
41698             }
41699         }
41700     },
41701
41702     // private
41703     onSelect : function(record, index){
41704         if(this.fireEvent('beforeselect', this, record, index) !== false){
41705             this.setFromData(index > -1 ? record.data : false);
41706             this.collapse();
41707             this.fireEvent('select', this, record, index);
41708         }
41709     },
41710
41711     /**
41712      * Returns the currently selected field value or empty string if no value is set.
41713      * @return {String} value The selected value
41714      */
41715     getValue : function(){
41716         if(this.valueField){
41717             return typeof this.value != 'undefined' ? this.value : '';
41718         }
41719         return Roo.form.ComboBox.superclass.getValue.call(this);
41720     },
41721
41722     /**
41723      * Clears any text/value currently set in the field
41724      */
41725     clearValue : function(){
41726         if(this.hiddenField){
41727             this.hiddenField.value = '';
41728         }
41729         this.value = '';
41730         this.setRawValue('');
41731         this.lastSelectionText = '';
41732         
41733     },
41734
41735     /**
41736      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41737      * will be displayed in the field.  If the value does not match the data value of an existing item,
41738      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41739      * Otherwise the field will be blank (although the value will still be set).
41740      * @param {String} value The value to match
41741      */
41742     setValue : function(v){
41743         var text = v;
41744         if(this.valueField){
41745             var r = this.findRecord(this.valueField, v);
41746             if(r){
41747                 text = r.data[this.displayField];
41748             }else if(this.valueNotFoundText !== undefined){
41749                 text = this.valueNotFoundText;
41750             }
41751         }
41752         this.lastSelectionText = text;
41753         if(this.hiddenField){
41754             this.hiddenField.value = v;
41755         }
41756         Roo.form.ComboBox.superclass.setValue.call(this, text);
41757         this.value = v;
41758     },
41759     /**
41760      * @property {Object} the last set data for the element
41761      */
41762     
41763     lastData : false,
41764     /**
41765      * Sets the value of the field based on a object which is related to the record format for the store.
41766      * @param {Object} value the value to set as. or false on reset?
41767      */
41768     setFromData : function(o){
41769         var dv = ''; // display value
41770         var vv = ''; // value value..
41771         this.lastData = o;
41772         if (this.displayField) {
41773             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41774         } else {
41775             // this is an error condition!!!
41776             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41777         }
41778         
41779         if(this.valueField){
41780             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41781         }
41782         if(this.hiddenField){
41783             this.hiddenField.value = vv;
41784             
41785             this.lastSelectionText = dv;
41786             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41787             this.value = vv;
41788             return;
41789         }
41790         // no hidden field.. - we store the value in 'value', but still display
41791         // display field!!!!
41792         this.lastSelectionText = dv;
41793         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41794         this.value = vv;
41795         
41796         
41797     },
41798     // private
41799     reset : function(){
41800         // overridden so that last data is reset..
41801         this.setValue(this.resetValue);
41802         this.originalValue = this.getValue();
41803         this.clearInvalid();
41804         this.lastData = false;
41805         if (this.view) {
41806             this.view.clearSelections();
41807         }
41808     },
41809     // private
41810     findRecord : function(prop, value){
41811         var record;
41812         if(this.store.getCount() > 0){
41813             this.store.each(function(r){
41814                 if(r.data[prop] == value){
41815                     record = r;
41816                     return false;
41817                 }
41818                 return true;
41819             });
41820         }
41821         return record;
41822     },
41823     
41824     getName: function()
41825     {
41826         // returns hidden if it's set..
41827         if (!this.rendered) {return ''};
41828         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41829         
41830     },
41831     // private
41832     onViewMove : function(e, t){
41833         this.inKeyMode = false;
41834     },
41835
41836     // private
41837     onViewOver : function(e, t){
41838         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41839             return;
41840         }
41841         var item = this.view.findItemFromChild(t);
41842         if(item){
41843             var index = this.view.indexOf(item);
41844             this.select(index, false);
41845         }
41846     },
41847
41848     // private
41849     onViewClick : function(doFocus)
41850     {
41851         var index = this.view.getSelectedIndexes()[0];
41852         var r = this.store.getAt(index);
41853         if(r){
41854             this.onSelect(r, index);
41855         }
41856         if(doFocus !== false && !this.blockFocus){
41857             this.el.focus();
41858         }
41859     },
41860
41861     // private
41862     restrictHeight : function(){
41863         this.innerList.dom.style.height = '';
41864         var inner = this.innerList.dom;
41865         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41866         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41867         this.list.beginUpdate();
41868         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41869         this.list.alignTo(this.el, this.listAlign);
41870         this.list.endUpdate();
41871     },
41872
41873     // private
41874     onEmptyResults : function(){
41875         this.collapse();
41876     },
41877
41878     /**
41879      * Returns true if the dropdown list is expanded, else false.
41880      */
41881     isExpanded : function(){
41882         return this.list.isVisible();
41883     },
41884
41885     /**
41886      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41887      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41888      * @param {String} value The data value of the item to select
41889      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41890      * selected item if it is not currently in view (defaults to true)
41891      * @return {Boolean} True if the value matched an item in the list, else false
41892      */
41893     selectByValue : function(v, scrollIntoView){
41894         if(v !== undefined && v !== null){
41895             var r = this.findRecord(this.valueField || this.displayField, v);
41896             if(r){
41897                 this.select(this.store.indexOf(r), scrollIntoView);
41898                 return true;
41899             }
41900         }
41901         return false;
41902     },
41903
41904     /**
41905      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41906      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41907      * @param {Number} index The zero-based index of the list item to select
41908      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41909      * selected item if it is not currently in view (defaults to true)
41910      */
41911     select : function(index, scrollIntoView){
41912         this.selectedIndex = index;
41913         this.view.select(index);
41914         if(scrollIntoView !== false){
41915             var el = this.view.getNode(index);
41916             if(el){
41917                 this.innerList.scrollChildIntoView(el, false);
41918             }
41919         }
41920     },
41921
41922     // private
41923     selectNext : function(){
41924         var ct = this.store.getCount();
41925         if(ct > 0){
41926             if(this.selectedIndex == -1){
41927                 this.select(0);
41928             }else if(this.selectedIndex < ct-1){
41929                 this.select(this.selectedIndex+1);
41930             }
41931         }
41932     },
41933
41934     // private
41935     selectPrev : function(){
41936         var ct = this.store.getCount();
41937         if(ct > 0){
41938             if(this.selectedIndex == -1){
41939                 this.select(0);
41940             }else if(this.selectedIndex != 0){
41941                 this.select(this.selectedIndex-1);
41942             }
41943         }
41944     },
41945
41946     // private
41947     onKeyUp : function(e){
41948         if(this.editable !== false && !e.isSpecialKey()){
41949             this.lastKey = e.getKey();
41950             this.dqTask.delay(this.queryDelay);
41951         }
41952     },
41953
41954     // private
41955     validateBlur : function(){
41956         return !this.list || !this.list.isVisible();   
41957     },
41958
41959     // private
41960     initQuery : function(){
41961         this.doQuery(this.getRawValue());
41962     },
41963
41964     // private
41965     doForce : function(){
41966         if(this.el.dom.value.length > 0){
41967             this.el.dom.value =
41968                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41969              
41970         }
41971     },
41972
41973     /**
41974      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41975      * query allowing the query action to be canceled if needed.
41976      * @param {String} query The SQL query to execute
41977      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41978      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41979      * saved in the current store (defaults to false)
41980      */
41981     doQuery : function(q, forceAll){
41982         if(q === undefined || q === null){
41983             q = '';
41984         }
41985         var qe = {
41986             query: q,
41987             forceAll: forceAll,
41988             combo: this,
41989             cancel:false
41990         };
41991         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41992             return false;
41993         }
41994         q = qe.query;
41995         forceAll = qe.forceAll;
41996         if(forceAll === true || (q.length >= this.minChars)){
41997             if(this.lastQuery != q || this.alwaysQuery){
41998                 this.lastQuery = q;
41999                 if(this.mode == 'local'){
42000                     this.selectedIndex = -1;
42001                     if(forceAll){
42002                         this.store.clearFilter();
42003                     }else{
42004                         this.store.filter(this.displayField, q);
42005                     }
42006                     this.onLoad();
42007                 }else{
42008                     this.store.baseParams[this.queryParam] = q;
42009                     this.store.load({
42010                         params: this.getParams(q)
42011                     });
42012                     this.expand();
42013                 }
42014             }else{
42015                 this.selectedIndex = -1;
42016                 this.onLoad();   
42017             }
42018         }
42019     },
42020
42021     // private
42022     getParams : function(q){
42023         var p = {};
42024         //p[this.queryParam] = q;
42025         if(this.pageSize){
42026             p.start = 0;
42027             p.limit = this.pageSize;
42028         }
42029         return p;
42030     },
42031
42032     /**
42033      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42034      */
42035     collapse : function(){
42036         if(!this.isExpanded()){
42037             return;
42038         }
42039         this.list.hide();
42040         Roo.get(document).un('mousedown', this.collapseIf, this);
42041         Roo.get(document).un('mousewheel', this.collapseIf, this);
42042         if (!this.editable) {
42043             Roo.get(document).un('keydown', this.listKeyPress, this);
42044         }
42045         this.fireEvent('collapse', this);
42046     },
42047
42048     // private
42049     collapseIf : function(e){
42050         if(!e.within(this.wrap) && !e.within(this.list)){
42051             this.collapse();
42052         }
42053     },
42054
42055     /**
42056      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42057      */
42058     expand : function(){
42059         if(this.isExpanded() || !this.hasFocus){
42060             return;
42061         }
42062         this.list.alignTo(this.el, this.listAlign);
42063         this.list.show();
42064         Roo.get(document).on('mousedown', this.collapseIf, this);
42065         Roo.get(document).on('mousewheel', this.collapseIf, this);
42066         if (!this.editable) {
42067             Roo.get(document).on('keydown', this.listKeyPress, this);
42068         }
42069         
42070         this.fireEvent('expand', this);
42071     },
42072
42073     // private
42074     // Implements the default empty TriggerField.onTriggerClick function
42075     onTriggerClick : function(){
42076         if(this.disabled){
42077             return;
42078         }
42079         if(this.isExpanded()){
42080             this.collapse();
42081             if (!this.blockFocus) {
42082                 this.el.focus();
42083             }
42084             
42085         }else {
42086             this.hasFocus = true;
42087             if(this.triggerAction == 'all') {
42088                 this.doQuery(this.allQuery, true);
42089             } else {
42090                 this.doQuery(this.getRawValue());
42091             }
42092             if (!this.blockFocus) {
42093                 this.el.focus();
42094             }
42095         }
42096     },
42097     listKeyPress : function(e)
42098     {
42099         //Roo.log('listkeypress');
42100         // scroll to first matching element based on key pres..
42101         if (e.isSpecialKey()) {
42102             return false;
42103         }
42104         var k = String.fromCharCode(e.getKey()).toUpperCase();
42105         //Roo.log(k);
42106         var match  = false;
42107         var csel = this.view.getSelectedNodes();
42108         var cselitem = false;
42109         if (csel.length) {
42110             var ix = this.view.indexOf(csel[0]);
42111             cselitem  = this.store.getAt(ix);
42112             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42113                 cselitem = false;
42114             }
42115             
42116         }
42117         
42118         this.store.each(function(v) { 
42119             if (cselitem) {
42120                 // start at existing selection.
42121                 if (cselitem.id == v.id) {
42122                     cselitem = false;
42123                 }
42124                 return;
42125             }
42126                 
42127             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42128                 match = this.store.indexOf(v);
42129                 return false;
42130             }
42131         }, this);
42132         
42133         if (match === false) {
42134             return true; // no more action?
42135         }
42136         // scroll to?
42137         this.view.select(match);
42138         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42139         sn.scrollIntoView(sn.dom.parentNode, false);
42140     } 
42141
42142     /** 
42143     * @cfg {Boolean} grow 
42144     * @hide 
42145     */
42146     /** 
42147     * @cfg {Number} growMin 
42148     * @hide 
42149     */
42150     /** 
42151     * @cfg {Number} growMax 
42152     * @hide 
42153     */
42154     /**
42155      * @hide
42156      * @method autoSize
42157      */
42158 });/*
42159  * Copyright(c) 2010-2012, Roo J Solutions Limited
42160  *
42161  * Licence LGPL
42162  *
42163  */
42164
42165 /**
42166  * @class Roo.form.ComboBoxArray
42167  * @extends Roo.form.TextField
42168  * A facebook style adder... for lists of email / people / countries  etc...
42169  * pick multiple items from a combo box, and shows each one.
42170  *
42171  *  Fred [x]  Brian [x]  [Pick another |v]
42172  *
42173  *
42174  *  For this to work: it needs various extra information
42175  *    - normal combo problay has
42176  *      name, hiddenName
42177  *    + displayField, valueField
42178  *
42179  *    For our purpose...
42180  *
42181  *
42182  *   If we change from 'extends' to wrapping...
42183  *   
42184  *  
42185  *
42186  
42187  
42188  * @constructor
42189  * Create a new ComboBoxArray.
42190  * @param {Object} config Configuration options
42191  */
42192  
42193
42194 Roo.form.ComboBoxArray = function(config)
42195 {
42196     this.addEvents({
42197         /**
42198          * @event beforeremove
42199          * Fires before remove the value from the list
42200              * @param {Roo.form.ComboBoxArray} _self This combo box array
42201              * @param {Roo.form.ComboBoxArray.Item} item removed item
42202              */
42203         'beforeremove' : true,
42204         /**
42205          * @event remove
42206          * Fires when remove the value from the list
42207              * @param {Roo.form.ComboBoxArray} _self This combo box array
42208              * @param {Roo.form.ComboBoxArray.Item} item removed item
42209              */
42210         'remove' : true
42211         
42212         
42213     });
42214     
42215     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42216     
42217     this.items = new Roo.util.MixedCollection(false);
42218     
42219     // construct the child combo...
42220     
42221     
42222     
42223     
42224    
42225     
42226 }
42227
42228  
42229 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42230
42231     /**
42232      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42233      */
42234     
42235     lastData : false,
42236     
42237     // behavies liek a hiddne field
42238     inputType:      'hidden',
42239     /**
42240      * @cfg {Number} width The width of the box that displays the selected element
42241      */ 
42242     width:          300,
42243
42244     
42245     
42246     /**
42247      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42248      */
42249     name : false,
42250     /**
42251      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42252      */
42253     hiddenName : false,
42254     
42255     
42256     // private the array of items that are displayed..
42257     items  : false,
42258     // private - the hidden field el.
42259     hiddenEl : false,
42260     // private - the filed el..
42261     el : false,
42262     
42263     //validateValue : function() { return true; }, // all values are ok!
42264     //onAddClick: function() { },
42265     
42266     onRender : function(ct, position) 
42267     {
42268         
42269         // create the standard hidden element
42270         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42271         
42272         
42273         // give fake names to child combo;
42274         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42275         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42276         
42277         this.combo = Roo.factory(this.combo, Roo.form);
42278         this.combo.onRender(ct, position);
42279         if (typeof(this.combo.width) != 'undefined') {
42280             this.combo.onResize(this.combo.width,0);
42281         }
42282         
42283         this.combo.initEvents();
42284         
42285         // assigned so form know we need to do this..
42286         this.store          = this.combo.store;
42287         this.valueField     = this.combo.valueField;
42288         this.displayField   = this.combo.displayField ;
42289         
42290         
42291         this.combo.wrap.addClass('x-cbarray-grp');
42292         
42293         var cbwrap = this.combo.wrap.createChild(
42294             {tag: 'div', cls: 'x-cbarray-cb'},
42295             this.combo.el.dom
42296         );
42297         
42298              
42299         this.hiddenEl = this.combo.wrap.createChild({
42300             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42301         });
42302         this.el = this.combo.wrap.createChild({
42303             tag: 'input',  type:'hidden' , name: this.name, value : ''
42304         });
42305          //   this.el.dom.removeAttribute("name");
42306         
42307         
42308         this.outerWrap = this.combo.wrap;
42309         this.wrap = cbwrap;
42310         
42311         this.outerWrap.setWidth(this.width);
42312         this.outerWrap.dom.removeChild(this.el.dom);
42313         
42314         this.wrap.dom.appendChild(this.el.dom);
42315         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42316         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42317         
42318         this.combo.trigger.setStyle('position','relative');
42319         this.combo.trigger.setStyle('left', '0px');
42320         this.combo.trigger.setStyle('top', '2px');
42321         
42322         this.combo.el.setStyle('vertical-align', 'text-bottom');
42323         
42324         //this.trigger.setStyle('vertical-align', 'top');
42325         
42326         // this should use the code from combo really... on('add' ....)
42327         if (this.adder) {
42328             
42329         
42330             this.adder = this.outerWrap.createChild(
42331                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42332             var _t = this;
42333             this.adder.on('click', function(e) {
42334                 _t.fireEvent('adderclick', this, e);
42335             }, _t);
42336         }
42337         //var _t = this;
42338         //this.adder.on('click', this.onAddClick, _t);
42339         
42340         
42341         this.combo.on('select', function(cb, rec, ix) {
42342             this.addItem(rec.data);
42343             
42344             cb.setValue('');
42345             cb.el.dom.value = '';
42346             //cb.lastData = rec.data;
42347             // add to list
42348             
42349         }, this);
42350         
42351         
42352     },
42353     
42354     
42355     getName: function()
42356     {
42357         // returns hidden if it's set..
42358         if (!this.rendered) {return ''};
42359         return  this.hiddenName ? this.hiddenName : this.name;
42360         
42361     },
42362     
42363     
42364     onResize: function(w, h){
42365         
42366         return;
42367         // not sure if this is needed..
42368         //this.combo.onResize(w,h);
42369         
42370         if(typeof w != 'number'){
42371             // we do not handle it!?!?
42372             return;
42373         }
42374         var tw = this.combo.trigger.getWidth();
42375         tw += this.addicon ? this.addicon.getWidth() : 0;
42376         tw += this.editicon ? this.editicon.getWidth() : 0;
42377         var x = w - tw;
42378         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42379             
42380         this.combo.trigger.setStyle('left', '0px');
42381         
42382         if(this.list && this.listWidth === undefined){
42383             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42384             this.list.setWidth(lw);
42385             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42386         }
42387         
42388     
42389         
42390     },
42391     
42392     addItem: function(rec)
42393     {
42394         var valueField = this.combo.valueField;
42395         var displayField = this.combo.displayField;
42396         
42397         if (this.items.indexOfKey(rec[valueField]) > -1) {
42398             //console.log("GOT " + rec.data.id);
42399             return;
42400         }
42401         
42402         var x = new Roo.form.ComboBoxArray.Item({
42403             //id : rec[this.idField],
42404             data : rec,
42405             displayField : displayField ,
42406             tipField : displayField ,
42407             cb : this
42408         });
42409         // use the 
42410         this.items.add(rec[valueField],x);
42411         // add it before the element..
42412         this.updateHiddenEl();
42413         x.render(this.outerWrap, this.wrap.dom);
42414         // add the image handler..
42415     },
42416     
42417     updateHiddenEl : function()
42418     {
42419         this.validate();
42420         if (!this.hiddenEl) {
42421             return;
42422         }
42423         var ar = [];
42424         var idField = this.combo.valueField;
42425         
42426         this.items.each(function(f) {
42427             ar.push(f.data[idField]);
42428         });
42429         this.hiddenEl.dom.value = ar.join(',');
42430         this.validate();
42431     },
42432     
42433     reset : function()
42434     {
42435         this.items.clear();
42436         
42437         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42438            el.remove();
42439         });
42440         
42441         this.el.dom.value = '';
42442         if (this.hiddenEl) {
42443             this.hiddenEl.dom.value = '';
42444         }
42445         
42446     },
42447     getValue: function()
42448     {
42449         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42450     },
42451     setValue: function(v) // not a valid action - must use addItems..
42452     {
42453         
42454         this.reset();
42455          
42456         if (this.store.isLocal && (typeof(v) == 'string')) {
42457             // then we can use the store to find the values..
42458             // comma seperated at present.. this needs to allow JSON based encoding..
42459             this.hiddenEl.value  = v;
42460             var v_ar = [];
42461             Roo.each(v.split(','), function(k) {
42462                 Roo.log("CHECK " + this.valueField + ',' + k);
42463                 var li = this.store.query(this.valueField, k);
42464                 if (!li.length) {
42465                     return;
42466                 }
42467                 var add = {};
42468                 add[this.valueField] = k;
42469                 add[this.displayField] = li.item(0).data[this.displayField];
42470                 
42471                 this.addItem(add);
42472             }, this) 
42473              
42474         }
42475         if (typeof(v) == 'object' ) {
42476             // then let's assume it's an array of objects..
42477             Roo.each(v, function(l) {
42478                 this.addItem(l);
42479             }, this);
42480              
42481         }
42482         
42483         
42484     },
42485     setFromData: function(v)
42486     {
42487         // this recieves an object, if setValues is called.
42488         this.reset();
42489         this.el.dom.value = v[this.displayField];
42490         this.hiddenEl.dom.value = v[this.valueField];
42491         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42492             return;
42493         }
42494         var kv = v[this.valueField];
42495         var dv = v[this.displayField];
42496         kv = typeof(kv) != 'string' ? '' : kv;
42497         dv = typeof(dv) != 'string' ? '' : dv;
42498         
42499         
42500         var keys = kv.split(',');
42501         var display = dv.split(',');
42502         for (var i = 0 ; i < keys.length; i++) {
42503             
42504             add = {};
42505             add[this.valueField] = keys[i];
42506             add[this.displayField] = display[i];
42507             this.addItem(add);
42508         }
42509       
42510         
42511     },
42512     
42513     /**
42514      * Validates the combox array value
42515      * @return {Boolean} True if the value is valid, else false
42516      */
42517     validate : function(){
42518         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42519             this.clearInvalid();
42520             return true;
42521         }
42522         return false;
42523     },
42524     
42525     validateValue : function(value){
42526         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42527         
42528     },
42529     
42530     /*@
42531      * overide
42532      * 
42533      */
42534     isDirty : function() {
42535         if(this.disabled) {
42536             return false;
42537         }
42538         
42539         try {
42540             var d = Roo.decode(String(this.originalValue));
42541         } catch (e) {
42542             return String(this.getValue()) !== String(this.originalValue);
42543         }
42544         
42545         var originalValue = [];
42546         
42547         for (var i = 0; i < d.length; i++){
42548             originalValue.push(d[i][this.valueField]);
42549         }
42550         
42551         return String(this.getValue()) !== String(originalValue.join(','));
42552         
42553     }
42554     
42555 });
42556
42557
42558
42559 /**
42560  * @class Roo.form.ComboBoxArray.Item
42561  * @extends Roo.BoxComponent
42562  * A selected item in the list
42563  *  Fred [x]  Brian [x]  [Pick another |v]
42564  * 
42565  * @constructor
42566  * Create a new item.
42567  * @param {Object} config Configuration options
42568  */
42569  
42570 Roo.form.ComboBoxArray.Item = function(config) {
42571     config.id = Roo.id();
42572     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42573 }
42574
42575 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42576     data : {},
42577     cb: false,
42578     displayField : false,
42579     tipField : false,
42580     
42581     
42582     defaultAutoCreate : {
42583         tag: 'div',
42584         cls: 'x-cbarray-item',
42585         cn : [ 
42586             { tag: 'div' },
42587             {
42588                 tag: 'img',
42589                 width:16,
42590                 height : 16,
42591                 src : Roo.BLANK_IMAGE_URL ,
42592                 align: 'center'
42593             }
42594         ]
42595         
42596     },
42597     
42598  
42599     onRender : function(ct, position)
42600     {
42601         Roo.form.Field.superclass.onRender.call(this, ct, position);
42602         
42603         if(!this.el){
42604             var cfg = this.getAutoCreate();
42605             this.el = ct.createChild(cfg, position);
42606         }
42607         
42608         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42609         
42610         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42611             this.cb.renderer(this.data) :
42612             String.format('{0}',this.data[this.displayField]);
42613         
42614             
42615         this.el.child('div').dom.setAttribute('qtip',
42616                         String.format('{0}',this.data[this.tipField])
42617         );
42618         
42619         this.el.child('img').on('click', this.remove, this);
42620         
42621     },
42622    
42623     remove : function()
42624     {
42625         if(this.cb.disabled){
42626             return;
42627         }
42628         
42629         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42630             this.cb.items.remove(this);
42631             this.el.child('img').un('click', this.remove, this);
42632             this.el.remove();
42633             this.cb.updateHiddenEl();
42634
42635             this.cb.fireEvent('remove', this.cb, this);
42636         }
42637         
42638     }
42639 });/*
42640  * Based on:
42641  * Ext JS Library 1.1.1
42642  * Copyright(c) 2006-2007, Ext JS, LLC.
42643  *
42644  * Originally Released Under LGPL - original licence link has changed is not relivant.
42645  *
42646  * Fork - LGPL
42647  * <script type="text/javascript">
42648  */
42649 /**
42650  * @class Roo.form.Checkbox
42651  * @extends Roo.form.Field
42652  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42653  * @constructor
42654  * Creates a new Checkbox
42655  * @param {Object} config Configuration options
42656  */
42657 Roo.form.Checkbox = function(config){
42658     Roo.form.Checkbox.superclass.constructor.call(this, config);
42659     this.addEvents({
42660         /**
42661          * @event check
42662          * Fires when the checkbox is checked or unchecked.
42663              * @param {Roo.form.Checkbox} this This checkbox
42664              * @param {Boolean} checked The new checked value
42665              */
42666         check : true
42667     });
42668 };
42669
42670 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42671     /**
42672      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42673      */
42674     focusClass : undefined,
42675     /**
42676      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42677      */
42678     fieldClass: "x-form-field",
42679     /**
42680      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42681      */
42682     checked: false,
42683     /**
42684      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42685      * {tag: "input", type: "checkbox", autocomplete: "off"})
42686      */
42687     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42688     /**
42689      * @cfg {String} boxLabel The text that appears beside the checkbox
42690      */
42691     boxLabel : "",
42692     /**
42693      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42694      */  
42695     inputValue : '1',
42696     /**
42697      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42698      */
42699      valueOff: '0', // value when not checked..
42700
42701     actionMode : 'viewEl', 
42702     //
42703     // private
42704     itemCls : 'x-menu-check-item x-form-item',
42705     groupClass : 'x-menu-group-item',
42706     inputType : 'hidden',
42707     
42708     
42709     inSetChecked: false, // check that we are not calling self...
42710     
42711     inputElement: false, // real input element?
42712     basedOn: false, // ????
42713     
42714     isFormField: true, // not sure where this is needed!!!!
42715
42716     onResize : function(){
42717         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42718         if(!this.boxLabel){
42719             this.el.alignTo(this.wrap, 'c-c');
42720         }
42721     },
42722
42723     initEvents : function(){
42724         Roo.form.Checkbox.superclass.initEvents.call(this);
42725         this.el.on("click", this.onClick,  this);
42726         this.el.on("change", this.onClick,  this);
42727     },
42728
42729
42730     getResizeEl : function(){
42731         return this.wrap;
42732     },
42733
42734     getPositionEl : function(){
42735         return this.wrap;
42736     },
42737
42738     // private
42739     onRender : function(ct, position){
42740         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42741         /*
42742         if(this.inputValue !== undefined){
42743             this.el.dom.value = this.inputValue;
42744         }
42745         */
42746         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42747         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42748         var viewEl = this.wrap.createChild({ 
42749             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42750         this.viewEl = viewEl;   
42751         this.wrap.on('click', this.onClick,  this); 
42752         
42753         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42754         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42755         
42756         
42757         
42758         if(this.boxLabel){
42759             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42760         //    viewEl.on('click', this.onClick,  this); 
42761         }
42762         //if(this.checked){
42763             this.setChecked(this.checked);
42764         //}else{
42765             //this.checked = this.el.dom;
42766         //}
42767
42768     },
42769
42770     // private
42771     initValue : Roo.emptyFn,
42772
42773     /**
42774      * Returns the checked state of the checkbox.
42775      * @return {Boolean} True if checked, else false
42776      */
42777     getValue : function(){
42778         if(this.el){
42779             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42780         }
42781         return this.valueOff;
42782         
42783     },
42784
42785         // private
42786     onClick : function(){ 
42787         if (this.disabled) {
42788             return;
42789         }
42790         this.setChecked(!this.checked);
42791
42792         //if(this.el.dom.checked != this.checked){
42793         //    this.setValue(this.el.dom.checked);
42794        // }
42795     },
42796
42797     /**
42798      * Sets the checked state of the checkbox.
42799      * On is always based on a string comparison between inputValue and the param.
42800      * @param {Boolean/String} value - the value to set 
42801      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42802      */
42803     setValue : function(v,suppressEvent){
42804         
42805         
42806         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42807         //if(this.el && this.el.dom){
42808         //    this.el.dom.checked = this.checked;
42809         //    this.el.dom.defaultChecked = this.checked;
42810         //}
42811         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42812         //this.fireEvent("check", this, this.checked);
42813     },
42814     // private..
42815     setChecked : function(state,suppressEvent)
42816     {
42817         if (this.inSetChecked) {
42818             this.checked = state;
42819             return;
42820         }
42821         
42822     
42823         if(this.wrap){
42824             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42825         }
42826         this.checked = state;
42827         if(suppressEvent !== true){
42828             this.fireEvent('check', this, state);
42829         }
42830         this.inSetChecked = true;
42831         this.el.dom.value = state ? this.inputValue : this.valueOff;
42832         this.inSetChecked = false;
42833         
42834     },
42835     // handle setting of hidden value by some other method!!?!?
42836     setFromHidden: function()
42837     {
42838         if(!this.el){
42839             return;
42840         }
42841         //console.log("SET FROM HIDDEN");
42842         //alert('setFrom hidden');
42843         this.setValue(this.el.dom.value);
42844     },
42845     
42846     onDestroy : function()
42847     {
42848         if(this.viewEl){
42849             Roo.get(this.viewEl).remove();
42850         }
42851          
42852         Roo.form.Checkbox.superclass.onDestroy.call(this);
42853     },
42854     
42855     setBoxLabel : function(str)
42856     {
42857         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42858     }
42859
42860 });/*
42861  * Based on:
42862  * Ext JS Library 1.1.1
42863  * Copyright(c) 2006-2007, Ext JS, LLC.
42864  *
42865  * Originally Released Under LGPL - original licence link has changed is not relivant.
42866  *
42867  * Fork - LGPL
42868  * <script type="text/javascript">
42869  */
42870  
42871 /**
42872  * @class Roo.form.Radio
42873  * @extends Roo.form.Checkbox
42874  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42875  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42876  * @constructor
42877  * Creates a new Radio
42878  * @param {Object} config Configuration options
42879  */
42880 Roo.form.Radio = function(){
42881     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42882 };
42883 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42884     inputType: 'radio',
42885
42886     /**
42887      * If this radio is part of a group, it will return the selected value
42888      * @return {String}
42889      */
42890     getGroupValue : function(){
42891         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42892     },
42893     
42894     
42895     onRender : function(ct, position){
42896         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42897         
42898         if(this.inputValue !== undefined){
42899             this.el.dom.value = this.inputValue;
42900         }
42901          
42902         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42903         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42904         //var viewEl = this.wrap.createChild({ 
42905         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42906         //this.viewEl = viewEl;   
42907         //this.wrap.on('click', this.onClick,  this); 
42908         
42909         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42910         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42911         
42912         
42913         
42914         if(this.boxLabel){
42915             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42916         //    viewEl.on('click', this.onClick,  this); 
42917         }
42918          if(this.checked){
42919             this.el.dom.checked =   'checked' ;
42920         }
42921          
42922     } 
42923     
42924     
42925 });//<script type="text/javascript">
42926
42927 /*
42928  * Based  Ext JS Library 1.1.1
42929  * Copyright(c) 2006-2007, Ext JS, LLC.
42930  * LGPL
42931  *
42932  */
42933  
42934 /**
42935  * @class Roo.HtmlEditorCore
42936  * @extends Roo.Component
42937  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42938  *
42939  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42940  */
42941
42942 Roo.HtmlEditorCore = function(config){
42943     
42944     
42945     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42946     
42947     
42948     this.addEvents({
42949         /**
42950          * @event initialize
42951          * Fires when the editor is fully initialized (including the iframe)
42952          * @param {Roo.HtmlEditorCore} this
42953          */
42954         initialize: true,
42955         /**
42956          * @event activate
42957          * Fires when the editor is first receives the focus. Any insertion must wait
42958          * until after this event.
42959          * @param {Roo.HtmlEditorCore} this
42960          */
42961         activate: true,
42962          /**
42963          * @event beforesync
42964          * Fires before the textarea is updated with content from the editor iframe. Return false
42965          * to cancel the sync.
42966          * @param {Roo.HtmlEditorCore} this
42967          * @param {String} html
42968          */
42969         beforesync: true,
42970          /**
42971          * @event beforepush
42972          * Fires before the iframe editor is updated with content from the textarea. Return false
42973          * to cancel the push.
42974          * @param {Roo.HtmlEditorCore} this
42975          * @param {String} html
42976          */
42977         beforepush: true,
42978          /**
42979          * @event sync
42980          * Fires when the textarea is updated with content from the editor iframe.
42981          * @param {Roo.HtmlEditorCore} this
42982          * @param {String} html
42983          */
42984         sync: true,
42985          /**
42986          * @event push
42987          * Fires when the iframe editor is updated with content from the textarea.
42988          * @param {Roo.HtmlEditorCore} this
42989          * @param {String} html
42990          */
42991         push: true,
42992         
42993         /**
42994          * @event editorevent
42995          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42996          * @param {Roo.HtmlEditorCore} this
42997          */
42998         editorevent: true
42999         
43000     });
43001     
43002     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43003     
43004     // defaults : white / black...
43005     this.applyBlacklists();
43006     
43007     
43008     
43009 };
43010
43011
43012 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43013
43014
43015      /**
43016      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43017      */
43018     
43019     owner : false,
43020     
43021      /**
43022      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43023      *                        Roo.resizable.
43024      */
43025     resizable : false,
43026      /**
43027      * @cfg {Number} height (in pixels)
43028      */   
43029     height: 300,
43030    /**
43031      * @cfg {Number} width (in pixels)
43032      */   
43033     width: 500,
43034     
43035     /**
43036      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43037      * 
43038      */
43039     stylesheets: false,
43040     
43041     // id of frame..
43042     frameId: false,
43043     
43044     // private properties
43045     validationEvent : false,
43046     deferHeight: true,
43047     initialized : false,
43048     activated : false,
43049     sourceEditMode : false,
43050     onFocus : Roo.emptyFn,
43051     iframePad:3,
43052     hideMode:'offsets',
43053     
43054     clearUp: true,
43055     
43056     // blacklist + whitelisted elements..
43057     black: false,
43058     white: false,
43059      
43060     bodyCls : '',
43061
43062     /**
43063      * Protected method that will not generally be called directly. It
43064      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43065      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43066      */
43067     getDocMarkup : function(){
43068         // body styles..
43069         var st = '';
43070         
43071         // inherit styels from page...?? 
43072         if (this.stylesheets === false) {
43073             
43074             Roo.get(document.head).select('style').each(function(node) {
43075                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43076             });
43077             
43078             Roo.get(document.head).select('link').each(function(node) { 
43079                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43080             });
43081             
43082         } else if (!this.stylesheets.length) {
43083                 // simple..
43084                 st = '<style type="text/css">' +
43085                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43086                    '</style>';
43087         } else { 
43088             st = '<style type="text/css">' +
43089                     this.stylesheets +
43090                 '</style>';
43091         }
43092         
43093         st +=  '<style type="text/css">' +
43094             'IMG { cursor: pointer } ' +
43095         '</style>';
43096
43097         var cls = 'roo-htmleditor-body';
43098         
43099         if(this.bodyCls.length){
43100             cls += ' ' + this.bodyCls;
43101         }
43102         
43103         return '<html><head>' + st  +
43104             //<style type="text/css">' +
43105             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43106             //'</style>' +
43107             ' </head><body class="' +  cls + '"></body></html>';
43108     },
43109
43110     // private
43111     onRender : function(ct, position)
43112     {
43113         var _t = this;
43114         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43115         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43116         
43117         
43118         this.el.dom.style.border = '0 none';
43119         this.el.dom.setAttribute('tabIndex', -1);
43120         this.el.addClass('x-hidden hide');
43121         
43122         
43123         
43124         if(Roo.isIE){ // fix IE 1px bogus margin
43125             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43126         }
43127        
43128         
43129         this.frameId = Roo.id();
43130         
43131          
43132         
43133         var iframe = this.owner.wrap.createChild({
43134             tag: 'iframe',
43135             cls: 'form-control', // bootstrap..
43136             id: this.frameId,
43137             name: this.frameId,
43138             frameBorder : 'no',
43139             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43140         }, this.el
43141         );
43142         
43143         
43144         this.iframe = iframe.dom;
43145
43146          this.assignDocWin();
43147         
43148         this.doc.designMode = 'on';
43149        
43150         this.doc.open();
43151         this.doc.write(this.getDocMarkup());
43152         this.doc.close();
43153
43154         
43155         var task = { // must defer to wait for browser to be ready
43156             run : function(){
43157                 //console.log("run task?" + this.doc.readyState);
43158                 this.assignDocWin();
43159                 if(this.doc.body || this.doc.readyState == 'complete'){
43160                     try {
43161                         this.doc.designMode="on";
43162                     } catch (e) {
43163                         return;
43164                     }
43165                     Roo.TaskMgr.stop(task);
43166                     this.initEditor.defer(10, this);
43167                 }
43168             },
43169             interval : 10,
43170             duration: 10000,
43171             scope: this
43172         };
43173         Roo.TaskMgr.start(task);
43174
43175     },
43176
43177     // private
43178     onResize : function(w, h)
43179     {
43180          Roo.log('resize: ' +w + ',' + h );
43181         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43182         if(!this.iframe){
43183             return;
43184         }
43185         if(typeof w == 'number'){
43186             
43187             this.iframe.style.width = w + 'px';
43188         }
43189         if(typeof h == 'number'){
43190             
43191             this.iframe.style.height = h + 'px';
43192             if(this.doc){
43193                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43194             }
43195         }
43196         
43197     },
43198
43199     /**
43200      * Toggles the editor between standard and source edit mode.
43201      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43202      */
43203     toggleSourceEdit : function(sourceEditMode){
43204         
43205         this.sourceEditMode = sourceEditMode === true;
43206         
43207         if(this.sourceEditMode){
43208  
43209             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43210             
43211         }else{
43212             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43213             //this.iframe.className = '';
43214             this.deferFocus();
43215         }
43216         //this.setSize(this.owner.wrap.getSize());
43217         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43218     },
43219
43220     
43221   
43222
43223     /**
43224      * Protected method that will not generally be called directly. If you need/want
43225      * custom HTML cleanup, this is the method you should override.
43226      * @param {String} html The HTML to be cleaned
43227      * return {String} The cleaned HTML
43228      */
43229     cleanHtml : function(html){
43230         html = String(html);
43231         if(html.length > 5){
43232             if(Roo.isSafari){ // strip safari nonsense
43233                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43234             }
43235         }
43236         if(html == '&nbsp;'){
43237             html = '';
43238         }
43239         return html;
43240     },
43241
43242     /**
43243      * HTML Editor -> Textarea
43244      * Protected method that will not generally be called directly. Syncs the contents
43245      * of the editor iframe with the textarea.
43246      */
43247     syncValue : function(){
43248         if(this.initialized){
43249             var bd = (this.doc.body || this.doc.documentElement);
43250             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43251             var html = bd.innerHTML;
43252             if(Roo.isSafari){
43253                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43254                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43255                 if(m && m[1]){
43256                     html = '<div style="'+m[0]+'">' + html + '</div>';
43257                 }
43258             }
43259             html = this.cleanHtml(html);
43260             // fix up the special chars.. normaly like back quotes in word...
43261             // however we do not want to do this with chinese..
43262             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43263                 
43264                 var cc = match.charCodeAt();
43265
43266                 // Get the character value, handling surrogate pairs
43267                 if (match.length == 2) {
43268                     // It's a surrogate pair, calculate the Unicode code point
43269                     var high = match.charCodeAt(0) - 0xD800;
43270                     var low  = match.charCodeAt(1) - 0xDC00;
43271                     cc = (high * 0x400) + low + 0x10000;
43272                 }  else if (
43273                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43274                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43275                     (cc >= 0xf900 && cc < 0xfb00 )
43276                 ) {
43277                         return match;
43278                 }  
43279          
43280                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43281                 return "&#" + cc + ";";
43282                 
43283                 
43284             });
43285             
43286             
43287              
43288             if(this.owner.fireEvent('beforesync', this, html) !== false){
43289                 this.el.dom.value = html;
43290                 this.owner.fireEvent('sync', this, html);
43291             }
43292         }
43293     },
43294
43295     /**
43296      * Protected method that will not generally be called directly. Pushes the value of the textarea
43297      * into the iframe editor.
43298      */
43299     pushValue : function(){
43300         if(this.initialized){
43301             var v = this.el.dom.value.trim();
43302             
43303 //            if(v.length < 1){
43304 //                v = '&#160;';
43305 //            }
43306             
43307             if(this.owner.fireEvent('beforepush', this, v) !== false){
43308                 var d = (this.doc.body || this.doc.documentElement);
43309                 d.innerHTML = v;
43310                 this.cleanUpPaste();
43311                 this.el.dom.value = d.innerHTML;
43312                 this.owner.fireEvent('push', this, v);
43313             }
43314         }
43315     },
43316
43317     // private
43318     deferFocus : function(){
43319         this.focus.defer(10, this);
43320     },
43321
43322     // doc'ed in Field
43323     focus : function(){
43324         if(this.win && !this.sourceEditMode){
43325             this.win.focus();
43326         }else{
43327             this.el.focus();
43328         }
43329     },
43330     
43331     assignDocWin: function()
43332     {
43333         var iframe = this.iframe;
43334         
43335          if(Roo.isIE){
43336             this.doc = iframe.contentWindow.document;
43337             this.win = iframe.contentWindow;
43338         } else {
43339 //            if (!Roo.get(this.frameId)) {
43340 //                return;
43341 //            }
43342 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43343 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43344             
43345             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43346                 return;
43347             }
43348             
43349             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43350             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43351         }
43352     },
43353     
43354     // private
43355     initEditor : function(){
43356         //console.log("INIT EDITOR");
43357         this.assignDocWin();
43358         
43359         
43360         
43361         this.doc.designMode="on";
43362         this.doc.open();
43363         this.doc.write(this.getDocMarkup());
43364         this.doc.close();
43365         
43366         var dbody = (this.doc.body || this.doc.documentElement);
43367         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43368         // this copies styles from the containing element into thsi one..
43369         // not sure why we need all of this..
43370         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43371         
43372         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43373         //ss['background-attachment'] = 'fixed'; // w3c
43374         dbody.bgProperties = 'fixed'; // ie
43375         //Roo.DomHelper.applyStyles(dbody, ss);
43376         Roo.EventManager.on(this.doc, {
43377             //'mousedown': this.onEditorEvent,
43378             'mouseup': this.onEditorEvent,
43379             'dblclick': this.onEditorEvent,
43380             'click': this.onEditorEvent,
43381             'keyup': this.onEditorEvent,
43382             buffer:100,
43383             scope: this
43384         });
43385         if(Roo.isGecko){
43386             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43387         }
43388         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43389             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43390         }
43391         this.initialized = true;
43392
43393         this.owner.fireEvent('initialize', this);
43394         this.pushValue();
43395     },
43396
43397     // private
43398     onDestroy : function(){
43399         
43400         
43401         
43402         if(this.rendered){
43403             
43404             //for (var i =0; i < this.toolbars.length;i++) {
43405             //    // fixme - ask toolbars for heights?
43406             //    this.toolbars[i].onDestroy();
43407            // }
43408             
43409             //this.wrap.dom.innerHTML = '';
43410             //this.wrap.remove();
43411         }
43412     },
43413
43414     // private
43415     onFirstFocus : function(){
43416         
43417         this.assignDocWin();
43418         
43419         
43420         this.activated = true;
43421          
43422     
43423         if(Roo.isGecko){ // prevent silly gecko errors
43424             this.win.focus();
43425             var s = this.win.getSelection();
43426             if(!s.focusNode || s.focusNode.nodeType != 3){
43427                 var r = s.getRangeAt(0);
43428                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43429                 r.collapse(true);
43430                 this.deferFocus();
43431             }
43432             try{
43433                 this.execCmd('useCSS', true);
43434                 this.execCmd('styleWithCSS', false);
43435             }catch(e){}
43436         }
43437         this.owner.fireEvent('activate', this);
43438     },
43439
43440     // private
43441     adjustFont: function(btn){
43442         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43443         //if(Roo.isSafari){ // safari
43444         //    adjust *= 2;
43445        // }
43446         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43447         if(Roo.isSafari){ // safari
43448             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43449             v =  (v < 10) ? 10 : v;
43450             v =  (v > 48) ? 48 : v;
43451             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43452             
43453         }
43454         
43455         
43456         v = Math.max(1, v+adjust);
43457         
43458         this.execCmd('FontSize', v  );
43459     },
43460
43461     onEditorEvent : function(e)
43462     {
43463         this.owner.fireEvent('editorevent', this, e);
43464       //  this.updateToolbar();
43465         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43466     },
43467
43468     insertTag : function(tg)
43469     {
43470         // could be a bit smarter... -> wrap the current selected tRoo..
43471         if (tg.toLowerCase() == 'span' ||
43472             tg.toLowerCase() == 'code' ||
43473             tg.toLowerCase() == 'sup' ||
43474             tg.toLowerCase() == 'sub' 
43475             ) {
43476             
43477             range = this.createRange(this.getSelection());
43478             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43479             wrappingNode.appendChild(range.extractContents());
43480             range.insertNode(wrappingNode);
43481
43482             return;
43483             
43484             
43485             
43486         }
43487         this.execCmd("formatblock",   tg);
43488         
43489     },
43490     
43491     insertText : function(txt)
43492     {
43493         
43494         
43495         var range = this.createRange();
43496         range.deleteContents();
43497                //alert(Sender.getAttribute('label'));
43498                
43499         range.insertNode(this.doc.createTextNode(txt));
43500     } ,
43501     
43502      
43503
43504     /**
43505      * Executes a Midas editor command on the editor document and performs necessary focus and
43506      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43507      * @param {String} cmd The Midas command
43508      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43509      */
43510     relayCmd : function(cmd, value){
43511         this.win.focus();
43512         this.execCmd(cmd, value);
43513         this.owner.fireEvent('editorevent', this);
43514         //this.updateToolbar();
43515         this.owner.deferFocus();
43516     },
43517
43518     /**
43519      * Executes a Midas editor command directly on the editor document.
43520      * For visual commands, you should use {@link #relayCmd} instead.
43521      * <b>This should only be called after the editor is initialized.</b>
43522      * @param {String} cmd The Midas command
43523      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43524      */
43525     execCmd : function(cmd, value){
43526         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43527         this.syncValue();
43528     },
43529  
43530  
43531    
43532     /**
43533      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43534      * to insert tRoo.
43535      * @param {String} text | dom node.. 
43536      */
43537     insertAtCursor : function(text)
43538     {
43539         
43540         if(!this.activated){
43541             return;
43542         }
43543         /*
43544         if(Roo.isIE){
43545             this.win.focus();
43546             var r = this.doc.selection.createRange();
43547             if(r){
43548                 r.collapse(true);
43549                 r.pasteHTML(text);
43550                 this.syncValue();
43551                 this.deferFocus();
43552             
43553             }
43554             return;
43555         }
43556         */
43557         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43558             this.win.focus();
43559             
43560             
43561             // from jquery ui (MIT licenced)
43562             var range, node;
43563             var win = this.win;
43564             
43565             if (win.getSelection && win.getSelection().getRangeAt) {
43566                 range = win.getSelection().getRangeAt(0);
43567                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43568                 range.insertNode(node);
43569             } else if (win.document.selection && win.document.selection.createRange) {
43570                 // no firefox support
43571                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43572                 win.document.selection.createRange().pasteHTML(txt);
43573             } else {
43574                 // no firefox support
43575                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43576                 this.execCmd('InsertHTML', txt);
43577             } 
43578             
43579             this.syncValue();
43580             
43581             this.deferFocus();
43582         }
43583     },
43584  // private
43585     mozKeyPress : function(e){
43586         if(e.ctrlKey){
43587             var c = e.getCharCode(), cmd;
43588           
43589             if(c > 0){
43590                 c = String.fromCharCode(c).toLowerCase();
43591                 switch(c){
43592                     case 'b':
43593                         cmd = 'bold';
43594                         break;
43595                     case 'i':
43596                         cmd = 'italic';
43597                         break;
43598                     
43599                     case 'u':
43600                         cmd = 'underline';
43601                         break;
43602                     
43603                     case 'v':
43604                         this.cleanUpPaste.defer(100, this);
43605                         return;
43606                         
43607                 }
43608                 if(cmd){
43609                     this.win.focus();
43610                     this.execCmd(cmd);
43611                     this.deferFocus();
43612                     e.preventDefault();
43613                 }
43614                 
43615             }
43616         }
43617     },
43618
43619     // private
43620     fixKeys : function(){ // load time branching for fastest keydown performance
43621         if(Roo.isIE){
43622             return function(e){
43623                 var k = e.getKey(), r;
43624                 if(k == e.TAB){
43625                     e.stopEvent();
43626                     r = this.doc.selection.createRange();
43627                     if(r){
43628                         r.collapse(true);
43629                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43630                         this.deferFocus();
43631                     }
43632                     return;
43633                 }
43634                 
43635                 if(k == e.ENTER){
43636                     r = this.doc.selection.createRange();
43637                     if(r){
43638                         var target = r.parentElement();
43639                         if(!target || target.tagName.toLowerCase() != 'li'){
43640                             e.stopEvent();
43641                             r.pasteHTML('<br />');
43642                             r.collapse(false);
43643                             r.select();
43644                         }
43645                     }
43646                 }
43647                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43648                     this.cleanUpPaste.defer(100, this);
43649                     return;
43650                 }
43651                 
43652                 
43653             };
43654         }else if(Roo.isOpera){
43655             return function(e){
43656                 var k = e.getKey();
43657                 if(k == e.TAB){
43658                     e.stopEvent();
43659                     this.win.focus();
43660                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43661                     this.deferFocus();
43662                 }
43663                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43664                     this.cleanUpPaste.defer(100, this);
43665                     return;
43666                 }
43667                 
43668             };
43669         }else if(Roo.isSafari){
43670             return function(e){
43671                 var k = e.getKey();
43672                 
43673                 if(k == e.TAB){
43674                     e.stopEvent();
43675                     this.execCmd('InsertText','\t');
43676                     this.deferFocus();
43677                     return;
43678                 }
43679                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43680                     this.cleanUpPaste.defer(100, this);
43681                     return;
43682                 }
43683                 
43684              };
43685         }
43686     }(),
43687     
43688     getAllAncestors: function()
43689     {
43690         var p = this.getSelectedNode();
43691         var a = [];
43692         if (!p) {
43693             a.push(p); // push blank onto stack..
43694             p = this.getParentElement();
43695         }
43696         
43697         
43698         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43699             a.push(p);
43700             p = p.parentNode;
43701         }
43702         a.push(this.doc.body);
43703         return a;
43704     },
43705     lastSel : false,
43706     lastSelNode : false,
43707     
43708     
43709     getSelection : function() 
43710     {
43711         this.assignDocWin();
43712         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43713     },
43714     
43715     getSelectedNode: function() 
43716     {
43717         // this may only work on Gecko!!!
43718         
43719         // should we cache this!!!!
43720         
43721         
43722         
43723          
43724         var range = this.createRange(this.getSelection()).cloneRange();
43725         
43726         if (Roo.isIE) {
43727             var parent = range.parentElement();
43728             while (true) {
43729                 var testRange = range.duplicate();
43730                 testRange.moveToElementText(parent);
43731                 if (testRange.inRange(range)) {
43732                     break;
43733                 }
43734                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43735                     break;
43736                 }
43737                 parent = parent.parentElement;
43738             }
43739             return parent;
43740         }
43741         
43742         // is ancestor a text element.
43743         var ac =  range.commonAncestorContainer;
43744         if (ac.nodeType == 3) {
43745             ac = ac.parentNode;
43746         }
43747         
43748         var ar = ac.childNodes;
43749          
43750         var nodes = [];
43751         var other_nodes = [];
43752         var has_other_nodes = false;
43753         for (var i=0;i<ar.length;i++) {
43754             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43755                 continue;
43756             }
43757             // fullly contained node.
43758             
43759             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43760                 nodes.push(ar[i]);
43761                 continue;
43762             }
43763             
43764             // probably selected..
43765             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43766                 other_nodes.push(ar[i]);
43767                 continue;
43768             }
43769             // outer..
43770             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43771                 continue;
43772             }
43773             
43774             
43775             has_other_nodes = true;
43776         }
43777         if (!nodes.length && other_nodes.length) {
43778             nodes= other_nodes;
43779         }
43780         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43781             return false;
43782         }
43783         
43784         return nodes[0];
43785     },
43786     createRange: function(sel)
43787     {
43788         // this has strange effects when using with 
43789         // top toolbar - not sure if it's a great idea.
43790         //this.editor.contentWindow.focus();
43791         if (typeof sel != "undefined") {
43792             try {
43793                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43794             } catch(e) {
43795                 return this.doc.createRange();
43796             }
43797         } else {
43798             return this.doc.createRange();
43799         }
43800     },
43801     getParentElement: function()
43802     {
43803         
43804         this.assignDocWin();
43805         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43806         
43807         var range = this.createRange(sel);
43808          
43809         try {
43810             var p = range.commonAncestorContainer;
43811             while (p.nodeType == 3) { // text node
43812                 p = p.parentNode;
43813             }
43814             return p;
43815         } catch (e) {
43816             return null;
43817         }
43818     
43819     },
43820     /***
43821      *
43822      * Range intersection.. the hard stuff...
43823      *  '-1' = before
43824      *  '0' = hits..
43825      *  '1' = after.
43826      *         [ -- selected range --- ]
43827      *   [fail]                        [fail]
43828      *
43829      *    basically..
43830      *      if end is before start or  hits it. fail.
43831      *      if start is after end or hits it fail.
43832      *
43833      *   if either hits (but other is outside. - then it's not 
43834      *   
43835      *    
43836      **/
43837     
43838     
43839     // @see http://www.thismuchiknow.co.uk/?p=64.
43840     rangeIntersectsNode : function(range, node)
43841     {
43842         var nodeRange = node.ownerDocument.createRange();
43843         try {
43844             nodeRange.selectNode(node);
43845         } catch (e) {
43846             nodeRange.selectNodeContents(node);
43847         }
43848     
43849         var rangeStartRange = range.cloneRange();
43850         rangeStartRange.collapse(true);
43851     
43852         var rangeEndRange = range.cloneRange();
43853         rangeEndRange.collapse(false);
43854     
43855         var nodeStartRange = nodeRange.cloneRange();
43856         nodeStartRange.collapse(true);
43857     
43858         var nodeEndRange = nodeRange.cloneRange();
43859         nodeEndRange.collapse(false);
43860     
43861         return rangeStartRange.compareBoundaryPoints(
43862                  Range.START_TO_START, nodeEndRange) == -1 &&
43863                rangeEndRange.compareBoundaryPoints(
43864                  Range.START_TO_START, nodeStartRange) == 1;
43865         
43866          
43867     },
43868     rangeCompareNode : function(range, node)
43869     {
43870         var nodeRange = node.ownerDocument.createRange();
43871         try {
43872             nodeRange.selectNode(node);
43873         } catch (e) {
43874             nodeRange.selectNodeContents(node);
43875         }
43876         
43877         
43878         range.collapse(true);
43879     
43880         nodeRange.collapse(true);
43881      
43882         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43883         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43884          
43885         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43886         
43887         var nodeIsBefore   =  ss == 1;
43888         var nodeIsAfter    = ee == -1;
43889         
43890         if (nodeIsBefore && nodeIsAfter) {
43891             return 0; // outer
43892         }
43893         if (!nodeIsBefore && nodeIsAfter) {
43894             return 1; //right trailed.
43895         }
43896         
43897         if (nodeIsBefore && !nodeIsAfter) {
43898             return 2;  // left trailed.
43899         }
43900         // fully contined.
43901         return 3;
43902     },
43903
43904     // private? - in a new class?
43905     cleanUpPaste :  function()
43906     {
43907         // cleans up the whole document..
43908         Roo.log('cleanuppaste');
43909         
43910         this.cleanUpChildren(this.doc.body);
43911         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43912         if (clean != this.doc.body.innerHTML) {
43913             this.doc.body.innerHTML = clean;
43914         }
43915         
43916     },
43917     
43918     cleanWordChars : function(input) {// change the chars to hex code
43919         var he = Roo.HtmlEditorCore;
43920         
43921         var output = input;
43922         Roo.each(he.swapCodes, function(sw) { 
43923             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43924             
43925             output = output.replace(swapper, sw[1]);
43926         });
43927         
43928         return output;
43929     },
43930     
43931     
43932     cleanUpChildren : function (n)
43933     {
43934         if (!n.childNodes.length) {
43935             return;
43936         }
43937         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43938            this.cleanUpChild(n.childNodes[i]);
43939         }
43940     },
43941     
43942     
43943         
43944     
43945     cleanUpChild : function (node)
43946     {
43947         var ed = this;
43948         //console.log(node);
43949         if (node.nodeName == "#text") {
43950             // clean up silly Windows -- stuff?
43951             return; 
43952         }
43953         if (node.nodeName == "#comment") {
43954             node.parentNode.removeChild(node);
43955             // clean up silly Windows -- stuff?
43956             return; 
43957         }
43958         var lcname = node.tagName.toLowerCase();
43959         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43960         // whitelist of tags..
43961         
43962         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43963             // remove node.
43964             node.parentNode.removeChild(node);
43965             return;
43966             
43967         }
43968         
43969         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43970         
43971         // spans with no attributes - just remove them..
43972         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
43973             remove_keep_children = true;
43974         }
43975         
43976         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43977         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43978         
43979         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43980         //    remove_keep_children = true;
43981         //}
43982         
43983         if (remove_keep_children) {
43984             this.cleanUpChildren(node);
43985             // inserts everything just before this node...
43986             while (node.childNodes.length) {
43987                 var cn = node.childNodes[0];
43988                 node.removeChild(cn);
43989                 node.parentNode.insertBefore(cn, node);
43990             }
43991             node.parentNode.removeChild(node);
43992             return;
43993         }
43994         
43995         if (!node.attributes || !node.attributes.length) {
43996             
43997           
43998             
43999             
44000             this.cleanUpChildren(node);
44001             return;
44002         }
44003         
44004         function cleanAttr(n,v)
44005         {
44006             
44007             if (v.match(/^\./) || v.match(/^\//)) {
44008                 return;
44009             }
44010             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44011                 return;
44012             }
44013             if (v.match(/^#/)) {
44014                 return;
44015             }
44016 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44017             node.removeAttribute(n);
44018             
44019         }
44020         
44021         var cwhite = this.cwhite;
44022         var cblack = this.cblack;
44023             
44024         function cleanStyle(n,v)
44025         {
44026             if (v.match(/expression/)) { //XSS?? should we even bother..
44027                 node.removeAttribute(n);
44028                 return;
44029             }
44030             
44031             var parts = v.split(/;/);
44032             var clean = [];
44033             
44034             Roo.each(parts, function(p) {
44035                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44036                 if (!p.length) {
44037                     return true;
44038                 }
44039                 var l = p.split(':').shift().replace(/\s+/g,'');
44040                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44041                 
44042                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44043 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44044                     //node.removeAttribute(n);
44045                     return true;
44046                 }
44047                 //Roo.log()
44048                 // only allow 'c whitelisted system attributes'
44049                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44050 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44051                     //node.removeAttribute(n);
44052                     return true;
44053                 }
44054                 
44055                 
44056                  
44057                 
44058                 clean.push(p);
44059                 return true;
44060             });
44061             if (clean.length) { 
44062                 node.setAttribute(n, clean.join(';'));
44063             } else {
44064                 node.removeAttribute(n);
44065             }
44066             
44067         }
44068         
44069         
44070         for (var i = node.attributes.length-1; i > -1 ; i--) {
44071             var a = node.attributes[i];
44072             //console.log(a);
44073             
44074             if (a.name.toLowerCase().substr(0,2)=='on')  {
44075                 node.removeAttribute(a.name);
44076                 continue;
44077             }
44078             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44079                 node.removeAttribute(a.name);
44080                 continue;
44081             }
44082             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44083                 cleanAttr(a.name,a.value); // fixme..
44084                 continue;
44085             }
44086             if (a.name == 'style') {
44087                 cleanStyle(a.name,a.value);
44088                 continue;
44089             }
44090             /// clean up MS crap..
44091             // tecnically this should be a list of valid class'es..
44092             
44093             
44094             if (a.name == 'class') {
44095                 if (a.value.match(/^Mso/)) {
44096                     node.removeAttribute('class');
44097                 }
44098                 
44099                 if (a.value.match(/^body$/)) {
44100                     node.removeAttribute('class');
44101                 }
44102                 continue;
44103             }
44104             
44105             // style cleanup!?
44106             // class cleanup?
44107             
44108         }
44109         
44110         
44111         this.cleanUpChildren(node);
44112         
44113         
44114     },
44115     
44116     /**
44117      * Clean up MS wordisms...
44118      */
44119     cleanWord : function(node)
44120     {
44121         if (!node) {
44122             this.cleanWord(this.doc.body);
44123             return;
44124         }
44125         
44126         if(
44127                 node.nodeName == 'SPAN' &&
44128                 !node.hasAttributes() &&
44129                 node.childNodes.length == 1 &&
44130                 node.firstChild.nodeName == "#text"  
44131         ) {
44132             var textNode = node.firstChild;
44133             node.removeChild(textNode);
44134             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44135                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44136             }
44137             node.parentNode.insertBefore(textNode, node);
44138             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44139                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44140             }
44141             node.parentNode.removeChild(node);
44142         }
44143         
44144         if (node.nodeName == "#text") {
44145             // clean up silly Windows -- stuff?
44146             return; 
44147         }
44148         if (node.nodeName == "#comment") {
44149             node.parentNode.removeChild(node);
44150             // clean up silly Windows -- stuff?
44151             return; 
44152         }
44153         
44154         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44155             node.parentNode.removeChild(node);
44156             return;
44157         }
44158         //Roo.log(node.tagName);
44159         // remove - but keep children..
44160         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44161             //Roo.log('-- removed');
44162             while (node.childNodes.length) {
44163                 var cn = node.childNodes[0];
44164                 node.removeChild(cn);
44165                 node.parentNode.insertBefore(cn, node);
44166                 // move node to parent - and clean it..
44167                 this.cleanWord(cn);
44168             }
44169             node.parentNode.removeChild(node);
44170             /// no need to iterate chidlren = it's got none..
44171             //this.iterateChildren(node, this.cleanWord);
44172             return;
44173         }
44174         // clean styles
44175         if (node.className.length) {
44176             
44177             var cn = node.className.split(/\W+/);
44178             var cna = [];
44179             Roo.each(cn, function(cls) {
44180                 if (cls.match(/Mso[a-zA-Z]+/)) {
44181                     return;
44182                 }
44183                 cna.push(cls);
44184             });
44185             node.className = cna.length ? cna.join(' ') : '';
44186             if (!cna.length) {
44187                 node.removeAttribute("class");
44188             }
44189         }
44190         
44191         if (node.hasAttribute("lang")) {
44192             node.removeAttribute("lang");
44193         }
44194         
44195         if (node.hasAttribute("style")) {
44196             
44197             var styles = node.getAttribute("style").split(";");
44198             var nstyle = [];
44199             Roo.each(styles, function(s) {
44200                 if (!s.match(/:/)) {
44201                     return;
44202                 }
44203                 var kv = s.split(":");
44204                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44205                     return;
44206                 }
44207                 // what ever is left... we allow.
44208                 nstyle.push(s);
44209             });
44210             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44211             if (!nstyle.length) {
44212                 node.removeAttribute('style');
44213             }
44214         }
44215         this.iterateChildren(node, this.cleanWord);
44216         
44217         
44218         
44219     },
44220     /**
44221      * iterateChildren of a Node, calling fn each time, using this as the scole..
44222      * @param {DomNode} node node to iterate children of.
44223      * @param {Function} fn method of this class to call on each item.
44224      */
44225     iterateChildren : function(node, fn)
44226     {
44227         if (!node.childNodes.length) {
44228                 return;
44229         }
44230         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44231            fn.call(this, node.childNodes[i])
44232         }
44233     },
44234     
44235     
44236     /**
44237      * cleanTableWidths.
44238      *
44239      * Quite often pasting from word etc.. results in tables with column and widths.
44240      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44241      *
44242      */
44243     cleanTableWidths : function(node)
44244     {
44245          
44246          
44247         if (!node) {
44248             this.cleanTableWidths(this.doc.body);
44249             return;
44250         }
44251         
44252         // ignore list...
44253         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44254             return; 
44255         }
44256         Roo.log(node.tagName);
44257         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44258             this.iterateChildren(node, this.cleanTableWidths);
44259             return;
44260         }
44261         if (node.hasAttribute('width')) {
44262             node.removeAttribute('width');
44263         }
44264         
44265          
44266         if (node.hasAttribute("style")) {
44267             // pretty basic...
44268             
44269             var styles = node.getAttribute("style").split(";");
44270             var nstyle = [];
44271             Roo.each(styles, function(s) {
44272                 if (!s.match(/:/)) {
44273                     return;
44274                 }
44275                 var kv = s.split(":");
44276                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44277                     return;
44278                 }
44279                 // what ever is left... we allow.
44280                 nstyle.push(s);
44281             });
44282             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44283             if (!nstyle.length) {
44284                 node.removeAttribute('style');
44285             }
44286         }
44287         
44288         this.iterateChildren(node, this.cleanTableWidths);
44289         
44290         
44291     },
44292     
44293     
44294     
44295     
44296     domToHTML : function(currentElement, depth, nopadtext) {
44297         
44298         depth = depth || 0;
44299         nopadtext = nopadtext || false;
44300     
44301         if (!currentElement) {
44302             return this.domToHTML(this.doc.body);
44303         }
44304         
44305         //Roo.log(currentElement);
44306         var j;
44307         var allText = false;
44308         var nodeName = currentElement.nodeName;
44309         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44310         
44311         if  (nodeName == '#text') {
44312             
44313             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44314         }
44315         
44316         
44317         var ret = '';
44318         if (nodeName != 'BODY') {
44319              
44320             var i = 0;
44321             // Prints the node tagName, such as <A>, <IMG>, etc
44322             if (tagName) {
44323                 var attr = [];
44324                 for(i = 0; i < currentElement.attributes.length;i++) {
44325                     // quoting?
44326                     var aname = currentElement.attributes.item(i).name;
44327                     if (!currentElement.attributes.item(i).value.length) {
44328                         continue;
44329                     }
44330                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44331                 }
44332                 
44333                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44334             } 
44335             else {
44336                 
44337                 // eack
44338             }
44339         } else {
44340             tagName = false;
44341         }
44342         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44343             return ret;
44344         }
44345         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44346             nopadtext = true;
44347         }
44348         
44349         
44350         // Traverse the tree
44351         i = 0;
44352         var currentElementChild = currentElement.childNodes.item(i);
44353         var allText = true;
44354         var innerHTML  = '';
44355         lastnode = '';
44356         while (currentElementChild) {
44357             // Formatting code (indent the tree so it looks nice on the screen)
44358             var nopad = nopadtext;
44359             if (lastnode == 'SPAN') {
44360                 nopad  = true;
44361             }
44362             // text
44363             if  (currentElementChild.nodeName == '#text') {
44364                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44365                 toadd = nopadtext ? toadd : toadd.trim();
44366                 if (!nopad && toadd.length > 80) {
44367                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44368                 }
44369                 innerHTML  += toadd;
44370                 
44371                 i++;
44372                 currentElementChild = currentElement.childNodes.item(i);
44373                 lastNode = '';
44374                 continue;
44375             }
44376             allText = false;
44377             
44378             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44379                 
44380             // Recursively traverse the tree structure of the child node
44381             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44382             lastnode = currentElementChild.nodeName;
44383             i++;
44384             currentElementChild=currentElement.childNodes.item(i);
44385         }
44386         
44387         ret += innerHTML;
44388         
44389         if (!allText) {
44390                 // The remaining code is mostly for formatting the tree
44391             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44392         }
44393         
44394         
44395         if (tagName) {
44396             ret+= "</"+tagName+">";
44397         }
44398         return ret;
44399         
44400     },
44401         
44402     applyBlacklists : function()
44403     {
44404         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44405         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44406         
44407         this.white = [];
44408         this.black = [];
44409         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44410             if (b.indexOf(tag) > -1) {
44411                 return;
44412             }
44413             this.white.push(tag);
44414             
44415         }, this);
44416         
44417         Roo.each(w, function(tag) {
44418             if (b.indexOf(tag) > -1) {
44419                 return;
44420             }
44421             if (this.white.indexOf(tag) > -1) {
44422                 return;
44423             }
44424             this.white.push(tag);
44425             
44426         }, this);
44427         
44428         
44429         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44430             if (w.indexOf(tag) > -1) {
44431                 return;
44432             }
44433             this.black.push(tag);
44434             
44435         }, this);
44436         
44437         Roo.each(b, function(tag) {
44438             if (w.indexOf(tag) > -1) {
44439                 return;
44440             }
44441             if (this.black.indexOf(tag) > -1) {
44442                 return;
44443             }
44444             this.black.push(tag);
44445             
44446         }, this);
44447         
44448         
44449         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44450         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44451         
44452         this.cwhite = [];
44453         this.cblack = [];
44454         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44455             if (b.indexOf(tag) > -1) {
44456                 return;
44457             }
44458             this.cwhite.push(tag);
44459             
44460         }, this);
44461         
44462         Roo.each(w, function(tag) {
44463             if (b.indexOf(tag) > -1) {
44464                 return;
44465             }
44466             if (this.cwhite.indexOf(tag) > -1) {
44467                 return;
44468             }
44469             this.cwhite.push(tag);
44470             
44471         }, this);
44472         
44473         
44474         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44475             if (w.indexOf(tag) > -1) {
44476                 return;
44477             }
44478             this.cblack.push(tag);
44479             
44480         }, this);
44481         
44482         Roo.each(b, function(tag) {
44483             if (w.indexOf(tag) > -1) {
44484                 return;
44485             }
44486             if (this.cblack.indexOf(tag) > -1) {
44487                 return;
44488             }
44489             this.cblack.push(tag);
44490             
44491         }, this);
44492     },
44493     
44494     setStylesheets : function(stylesheets)
44495     {
44496         if(typeof(stylesheets) == 'string'){
44497             Roo.get(this.iframe.contentDocument.head).createChild({
44498                 tag : 'link',
44499                 rel : 'stylesheet',
44500                 type : 'text/css',
44501                 href : stylesheets
44502             });
44503             
44504             return;
44505         }
44506         var _this = this;
44507      
44508         Roo.each(stylesheets, function(s) {
44509             if(!s.length){
44510                 return;
44511             }
44512             
44513             Roo.get(_this.iframe.contentDocument.head).createChild({
44514                 tag : 'link',
44515                 rel : 'stylesheet',
44516                 type : 'text/css',
44517                 href : s
44518             });
44519         });
44520
44521         
44522     },
44523     
44524     removeStylesheets : function()
44525     {
44526         var _this = this;
44527         
44528         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44529             s.remove();
44530         });
44531     },
44532     
44533     setStyle : function(style)
44534     {
44535         Roo.get(this.iframe.contentDocument.head).createChild({
44536             tag : 'style',
44537             type : 'text/css',
44538             html : style
44539         });
44540
44541         return;
44542     }
44543     
44544     // hide stuff that is not compatible
44545     /**
44546      * @event blur
44547      * @hide
44548      */
44549     /**
44550      * @event change
44551      * @hide
44552      */
44553     /**
44554      * @event focus
44555      * @hide
44556      */
44557     /**
44558      * @event specialkey
44559      * @hide
44560      */
44561     /**
44562      * @cfg {String} fieldClass @hide
44563      */
44564     /**
44565      * @cfg {String} focusClass @hide
44566      */
44567     /**
44568      * @cfg {String} autoCreate @hide
44569      */
44570     /**
44571      * @cfg {String} inputType @hide
44572      */
44573     /**
44574      * @cfg {String} invalidClass @hide
44575      */
44576     /**
44577      * @cfg {String} invalidText @hide
44578      */
44579     /**
44580      * @cfg {String} msgFx @hide
44581      */
44582     /**
44583      * @cfg {String} validateOnBlur @hide
44584      */
44585 });
44586
44587 Roo.HtmlEditorCore.white = [
44588         'area', 'br', 'img', 'input', 'hr', 'wbr',
44589         
44590        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44591        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44592        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44593        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44594        'table',   'ul',         'xmp', 
44595        
44596        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44597       'thead',   'tr', 
44598      
44599       'dir', 'menu', 'ol', 'ul', 'dl',
44600        
44601       'embed',  'object'
44602 ];
44603
44604
44605 Roo.HtmlEditorCore.black = [
44606     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44607         'applet', // 
44608         'base',   'basefont', 'bgsound', 'blink',  'body', 
44609         'frame',  'frameset', 'head',    'html',   'ilayer', 
44610         'iframe', 'layer',  'link',     'meta',    'object',   
44611         'script', 'style' ,'title',  'xml' // clean later..
44612 ];
44613 Roo.HtmlEditorCore.clean = [
44614     'script', 'style', 'title', 'xml'
44615 ];
44616 Roo.HtmlEditorCore.remove = [
44617     'font'
44618 ];
44619 // attributes..
44620
44621 Roo.HtmlEditorCore.ablack = [
44622     'on'
44623 ];
44624     
44625 Roo.HtmlEditorCore.aclean = [ 
44626     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44627 ];
44628
44629 // protocols..
44630 Roo.HtmlEditorCore.pwhite= [
44631         'http',  'https',  'mailto'
44632 ];
44633
44634 // white listed style attributes.
44635 Roo.HtmlEditorCore.cwhite= [
44636       //  'text-align', /// default is to allow most things..
44637       
44638          
44639 //        'font-size'//??
44640 ];
44641
44642 // black listed style attributes.
44643 Roo.HtmlEditorCore.cblack= [
44644       //  'font-size' -- this can be set by the project 
44645 ];
44646
44647
44648 Roo.HtmlEditorCore.swapCodes   =[ 
44649     [    8211, "--" ], 
44650     [    8212, "--" ], 
44651     [    8216,  "'" ],  
44652     [    8217, "'" ],  
44653     [    8220, '"' ],  
44654     [    8221, '"' ],  
44655     [    8226, "*" ],  
44656     [    8230, "..." ]
44657 ]; 
44658
44659     //<script type="text/javascript">
44660
44661 /*
44662  * Ext JS Library 1.1.1
44663  * Copyright(c) 2006-2007, Ext JS, LLC.
44664  * Licence LGPL
44665  * 
44666  */
44667  
44668  
44669 Roo.form.HtmlEditor = function(config){
44670     
44671     
44672     
44673     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44674     
44675     if (!this.toolbars) {
44676         this.toolbars = [];
44677     }
44678     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44679     
44680     
44681 };
44682
44683 /**
44684  * @class Roo.form.HtmlEditor
44685  * @extends Roo.form.Field
44686  * Provides a lightweight HTML Editor component.
44687  *
44688  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44689  * 
44690  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44691  * supported by this editor.</b><br/><br/>
44692  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44693  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44694  */
44695 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44696     /**
44697      * @cfg {Boolean} clearUp
44698      */
44699     clearUp : true,
44700       /**
44701      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44702      */
44703     toolbars : false,
44704    
44705      /**
44706      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44707      *                        Roo.resizable.
44708      */
44709     resizable : false,
44710      /**
44711      * @cfg {Number} height (in pixels)
44712      */   
44713     height: 300,
44714    /**
44715      * @cfg {Number} width (in pixels)
44716      */   
44717     width: 500,
44718     
44719     /**
44720      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44721      * 
44722      */
44723     stylesheets: false,
44724     
44725     
44726      /**
44727      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44728      * 
44729      */
44730     cblack: false,
44731     /**
44732      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44733      * 
44734      */
44735     cwhite: false,
44736     
44737      /**
44738      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44739      * 
44740      */
44741     black: false,
44742     /**
44743      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44744      * 
44745      */
44746     white: false,
44747     
44748     // id of frame..
44749     frameId: false,
44750     
44751     // private properties
44752     validationEvent : false,
44753     deferHeight: true,
44754     initialized : false,
44755     activated : false,
44756     
44757     onFocus : Roo.emptyFn,
44758     iframePad:3,
44759     hideMode:'offsets',
44760     
44761     actionMode : 'container', // defaults to hiding it...
44762     
44763     defaultAutoCreate : { // modified by initCompnoent..
44764         tag: "textarea",
44765         style:"width:500px;height:300px;",
44766         autocomplete: "new-password"
44767     },
44768
44769     // private
44770     initComponent : function(){
44771         this.addEvents({
44772             /**
44773              * @event initialize
44774              * Fires when the editor is fully initialized (including the iframe)
44775              * @param {HtmlEditor} this
44776              */
44777             initialize: true,
44778             /**
44779              * @event activate
44780              * Fires when the editor is first receives the focus. Any insertion must wait
44781              * until after this event.
44782              * @param {HtmlEditor} this
44783              */
44784             activate: true,
44785              /**
44786              * @event beforesync
44787              * Fires before the textarea is updated with content from the editor iframe. Return false
44788              * to cancel the sync.
44789              * @param {HtmlEditor} this
44790              * @param {String} html
44791              */
44792             beforesync: true,
44793              /**
44794              * @event beforepush
44795              * Fires before the iframe editor is updated with content from the textarea. Return false
44796              * to cancel the push.
44797              * @param {HtmlEditor} this
44798              * @param {String} html
44799              */
44800             beforepush: true,
44801              /**
44802              * @event sync
44803              * Fires when the textarea is updated with content from the editor iframe.
44804              * @param {HtmlEditor} this
44805              * @param {String} html
44806              */
44807             sync: true,
44808              /**
44809              * @event push
44810              * Fires when the iframe editor is updated with content from the textarea.
44811              * @param {HtmlEditor} this
44812              * @param {String} html
44813              */
44814             push: true,
44815              /**
44816              * @event editmodechange
44817              * Fires when the editor switches edit modes
44818              * @param {HtmlEditor} this
44819              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44820              */
44821             editmodechange: true,
44822             /**
44823              * @event editorevent
44824              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44825              * @param {HtmlEditor} this
44826              */
44827             editorevent: true,
44828             /**
44829              * @event firstfocus
44830              * Fires when on first focus - needed by toolbars..
44831              * @param {HtmlEditor} this
44832              */
44833             firstfocus: true,
44834             /**
44835              * @event autosave
44836              * Auto save the htmlEditor value as a file into Events
44837              * @param {HtmlEditor} this
44838              */
44839             autosave: true,
44840             /**
44841              * @event savedpreview
44842              * preview the saved version of htmlEditor
44843              * @param {HtmlEditor} this
44844              */
44845             savedpreview: true,
44846             
44847             /**
44848             * @event stylesheetsclick
44849             * Fires when press the Sytlesheets button
44850             * @param {Roo.HtmlEditorCore} this
44851             */
44852             stylesheetsclick: true
44853         });
44854         this.defaultAutoCreate =  {
44855             tag: "textarea",
44856             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44857             autocomplete: "new-password"
44858         };
44859     },
44860
44861     /**
44862      * Protected method that will not generally be called directly. It
44863      * is called when the editor creates its toolbar. Override this method if you need to
44864      * add custom toolbar buttons.
44865      * @param {HtmlEditor} editor
44866      */
44867     createToolbar : function(editor){
44868         Roo.log("create toolbars");
44869         if (!editor.toolbars || !editor.toolbars.length) {
44870             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44871         }
44872         
44873         for (var i =0 ; i < editor.toolbars.length;i++) {
44874             editor.toolbars[i] = Roo.factory(
44875                     typeof(editor.toolbars[i]) == 'string' ?
44876                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44877                 Roo.form.HtmlEditor);
44878             editor.toolbars[i].init(editor);
44879         }
44880          
44881         
44882     },
44883
44884      
44885     // private
44886     onRender : function(ct, position)
44887     {
44888         var _t = this;
44889         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44890         
44891         this.wrap = this.el.wrap({
44892             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44893         });
44894         
44895         this.editorcore.onRender(ct, position);
44896          
44897         if (this.resizable) {
44898             this.resizeEl = new Roo.Resizable(this.wrap, {
44899                 pinned : true,
44900                 wrap: true,
44901                 dynamic : true,
44902                 minHeight : this.height,
44903                 height: this.height,
44904                 handles : this.resizable,
44905                 width: this.width,
44906                 listeners : {
44907                     resize : function(r, w, h) {
44908                         _t.onResize(w,h); // -something
44909                     }
44910                 }
44911             });
44912             
44913         }
44914         this.createToolbar(this);
44915        
44916         
44917         if(!this.width){
44918             this.setSize(this.wrap.getSize());
44919         }
44920         if (this.resizeEl) {
44921             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44922             // should trigger onReize..
44923         }
44924         
44925         this.keyNav = new Roo.KeyNav(this.el, {
44926             
44927             "tab" : function(e){
44928                 e.preventDefault();
44929                 
44930                 var value = this.getValue();
44931                 
44932                 var start = this.el.dom.selectionStart;
44933                 var end = this.el.dom.selectionEnd;
44934                 
44935                 if(!e.shiftKey){
44936                     
44937                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44938                     this.el.dom.setSelectionRange(end + 1, end + 1);
44939                     return;
44940                 }
44941                 
44942                 var f = value.substring(0, start).split("\t");
44943                 
44944                 if(f.pop().length != 0){
44945                     return;
44946                 }
44947                 
44948                 this.setValue(f.join("\t") + value.substring(end));
44949                 this.el.dom.setSelectionRange(start - 1, start - 1);
44950                 
44951             },
44952             
44953             "home" : function(e){
44954                 e.preventDefault();
44955                 
44956                 var curr = this.el.dom.selectionStart;
44957                 var lines = this.getValue().split("\n");
44958                 
44959                 if(!lines.length){
44960                     return;
44961                 }
44962                 
44963                 if(e.ctrlKey){
44964                     this.el.dom.setSelectionRange(0, 0);
44965                     return;
44966                 }
44967                 
44968                 var pos = 0;
44969                 
44970                 for (var i = 0; i < lines.length;i++) {
44971                     pos += lines[i].length;
44972                     
44973                     if(i != 0){
44974                         pos += 1;
44975                     }
44976                     
44977                     if(pos < curr){
44978                         continue;
44979                     }
44980                     
44981                     pos -= lines[i].length;
44982                     
44983                     break;
44984                 }
44985                 
44986                 if(!e.shiftKey){
44987                     this.el.dom.setSelectionRange(pos, pos);
44988                     return;
44989                 }
44990                 
44991                 this.el.dom.selectionStart = pos;
44992                 this.el.dom.selectionEnd = curr;
44993             },
44994             
44995             "end" : function(e){
44996                 e.preventDefault();
44997                 
44998                 var curr = this.el.dom.selectionStart;
44999                 var lines = this.getValue().split("\n");
45000                 
45001                 if(!lines.length){
45002                     return;
45003                 }
45004                 
45005                 if(e.ctrlKey){
45006                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45007                     return;
45008                 }
45009                 
45010                 var pos = 0;
45011                 
45012                 for (var i = 0; i < lines.length;i++) {
45013                     
45014                     pos += lines[i].length;
45015                     
45016                     if(i != 0){
45017                         pos += 1;
45018                     }
45019                     
45020                     if(pos < curr){
45021                         continue;
45022                     }
45023                     
45024                     break;
45025                 }
45026                 
45027                 if(!e.shiftKey){
45028                     this.el.dom.setSelectionRange(pos, pos);
45029                     return;
45030                 }
45031                 
45032                 this.el.dom.selectionStart = curr;
45033                 this.el.dom.selectionEnd = pos;
45034             },
45035
45036             scope : this,
45037
45038             doRelay : function(foo, bar, hname){
45039                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45040             },
45041
45042             forceKeyDown: true
45043         });
45044         
45045 //        if(this.autosave && this.w){
45046 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45047 //        }
45048     },
45049
45050     // private
45051     onResize : function(w, h)
45052     {
45053         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45054         var ew = false;
45055         var eh = false;
45056         
45057         if(this.el ){
45058             if(typeof w == 'number'){
45059                 var aw = w - this.wrap.getFrameWidth('lr');
45060                 this.el.setWidth(this.adjustWidth('textarea', aw));
45061                 ew = aw;
45062             }
45063             if(typeof h == 'number'){
45064                 var tbh = 0;
45065                 for (var i =0; i < this.toolbars.length;i++) {
45066                     // fixme - ask toolbars for heights?
45067                     tbh += this.toolbars[i].tb.el.getHeight();
45068                     if (this.toolbars[i].footer) {
45069                         tbh += this.toolbars[i].footer.el.getHeight();
45070                     }
45071                 }
45072                 
45073                 
45074                 
45075                 
45076                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45077                 ah -= 5; // knock a few pixes off for look..
45078 //                Roo.log(ah);
45079                 this.el.setHeight(this.adjustWidth('textarea', ah));
45080                 var eh = ah;
45081             }
45082         }
45083         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45084         this.editorcore.onResize(ew,eh);
45085         
45086     },
45087
45088     /**
45089      * Toggles the editor between standard and source edit mode.
45090      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45091      */
45092     toggleSourceEdit : function(sourceEditMode)
45093     {
45094         this.editorcore.toggleSourceEdit(sourceEditMode);
45095         
45096         if(this.editorcore.sourceEditMode){
45097             Roo.log('editor - showing textarea');
45098             
45099 //            Roo.log('in');
45100 //            Roo.log(this.syncValue());
45101             this.editorcore.syncValue();
45102             this.el.removeClass('x-hidden');
45103             this.el.dom.removeAttribute('tabIndex');
45104             this.el.focus();
45105             
45106             for (var i = 0; i < this.toolbars.length; i++) {
45107                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45108                     this.toolbars[i].tb.hide();
45109                     this.toolbars[i].footer.hide();
45110                 }
45111             }
45112             
45113         }else{
45114             Roo.log('editor - hiding textarea');
45115 //            Roo.log('out')
45116 //            Roo.log(this.pushValue()); 
45117             this.editorcore.pushValue();
45118             
45119             this.el.addClass('x-hidden');
45120             this.el.dom.setAttribute('tabIndex', -1);
45121             
45122             for (var i = 0; i < this.toolbars.length; i++) {
45123                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45124                     this.toolbars[i].tb.show();
45125                     this.toolbars[i].footer.show();
45126                 }
45127             }
45128             
45129             //this.deferFocus();
45130         }
45131         
45132         this.setSize(this.wrap.getSize());
45133         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45134         
45135         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45136     },
45137  
45138     // private (for BoxComponent)
45139     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45140
45141     // private (for BoxComponent)
45142     getResizeEl : function(){
45143         return this.wrap;
45144     },
45145
45146     // private (for BoxComponent)
45147     getPositionEl : function(){
45148         return this.wrap;
45149     },
45150
45151     // private
45152     initEvents : function(){
45153         this.originalValue = this.getValue();
45154     },
45155
45156     /**
45157      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45158      * @method
45159      */
45160     markInvalid : Roo.emptyFn,
45161     /**
45162      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45163      * @method
45164      */
45165     clearInvalid : Roo.emptyFn,
45166
45167     setValue : function(v){
45168         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45169         this.editorcore.pushValue();
45170     },
45171
45172      
45173     // private
45174     deferFocus : function(){
45175         this.focus.defer(10, this);
45176     },
45177
45178     // doc'ed in Field
45179     focus : function(){
45180         this.editorcore.focus();
45181         
45182     },
45183       
45184
45185     // private
45186     onDestroy : function(){
45187         
45188         
45189         
45190         if(this.rendered){
45191             
45192             for (var i =0; i < this.toolbars.length;i++) {
45193                 // fixme - ask toolbars for heights?
45194                 this.toolbars[i].onDestroy();
45195             }
45196             
45197             this.wrap.dom.innerHTML = '';
45198             this.wrap.remove();
45199         }
45200     },
45201
45202     // private
45203     onFirstFocus : function(){
45204         //Roo.log("onFirstFocus");
45205         this.editorcore.onFirstFocus();
45206          for (var i =0; i < this.toolbars.length;i++) {
45207             this.toolbars[i].onFirstFocus();
45208         }
45209         
45210     },
45211     
45212     // private
45213     syncValue : function()
45214     {
45215         this.editorcore.syncValue();
45216     },
45217     
45218     pushValue : function()
45219     {
45220         this.editorcore.pushValue();
45221     },
45222     
45223     setStylesheets : function(stylesheets)
45224     {
45225         this.editorcore.setStylesheets(stylesheets);
45226     },
45227     
45228     removeStylesheets : function()
45229     {
45230         this.editorcore.removeStylesheets();
45231     }
45232      
45233     
45234     // hide stuff that is not compatible
45235     /**
45236      * @event blur
45237      * @hide
45238      */
45239     /**
45240      * @event change
45241      * @hide
45242      */
45243     /**
45244      * @event focus
45245      * @hide
45246      */
45247     /**
45248      * @event specialkey
45249      * @hide
45250      */
45251     /**
45252      * @cfg {String} fieldClass @hide
45253      */
45254     /**
45255      * @cfg {String} focusClass @hide
45256      */
45257     /**
45258      * @cfg {String} autoCreate @hide
45259      */
45260     /**
45261      * @cfg {String} inputType @hide
45262      */
45263     /**
45264      * @cfg {String} invalidClass @hide
45265      */
45266     /**
45267      * @cfg {String} invalidText @hide
45268      */
45269     /**
45270      * @cfg {String} msgFx @hide
45271      */
45272     /**
45273      * @cfg {String} validateOnBlur @hide
45274      */
45275 });
45276  
45277     // <script type="text/javascript">
45278 /*
45279  * Based on
45280  * Ext JS Library 1.1.1
45281  * Copyright(c) 2006-2007, Ext JS, LLC.
45282  *  
45283  
45284  */
45285
45286 /**
45287  * @class Roo.form.HtmlEditorToolbar1
45288  * Basic Toolbar
45289  * 
45290  * Usage:
45291  *
45292  new Roo.form.HtmlEditor({
45293     ....
45294     toolbars : [
45295         new Roo.form.HtmlEditorToolbar1({
45296             disable : { fonts: 1 , format: 1, ..., ... , ...],
45297             btns : [ .... ]
45298         })
45299     }
45300      
45301  * 
45302  * @cfg {Object} disable List of elements to disable..
45303  * @cfg {Array} btns List of additional buttons.
45304  * 
45305  * 
45306  * NEEDS Extra CSS? 
45307  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45308  */
45309  
45310 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45311 {
45312     
45313     Roo.apply(this, config);
45314     
45315     // default disabled, based on 'good practice'..
45316     this.disable = this.disable || {};
45317     Roo.applyIf(this.disable, {
45318         fontSize : true,
45319         colors : true,
45320         specialElements : true
45321     });
45322     
45323     
45324     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45325     // dont call parent... till later.
45326 }
45327
45328 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45329     
45330     tb: false,
45331     
45332     rendered: false,
45333     
45334     editor : false,
45335     editorcore : false,
45336     /**
45337      * @cfg {Object} disable  List of toolbar elements to disable
45338          
45339      */
45340     disable : false,
45341     
45342     
45343      /**
45344      * @cfg {String} createLinkText The default text for the create link prompt
45345      */
45346     createLinkText : 'Please enter the URL for the link:',
45347     /**
45348      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45349      */
45350     defaultLinkValue : 'http:/'+'/',
45351    
45352     
45353       /**
45354      * @cfg {Array} fontFamilies An array of available font families
45355      */
45356     fontFamilies : [
45357         'Arial',
45358         'Courier New',
45359         'Tahoma',
45360         'Times New Roman',
45361         'Verdana'
45362     ],
45363     
45364     specialChars : [
45365            "&#169;",
45366           "&#174;",     
45367           "&#8482;",    
45368           "&#163;" ,    
45369          // "&#8212;",    
45370           "&#8230;",    
45371           "&#247;" ,    
45372         //  "&#225;" ,     ?? a acute?
45373            "&#8364;"    , //Euro
45374        //   "&#8220;"    ,
45375         //  "&#8221;"    ,
45376         //  "&#8226;"    ,
45377           "&#176;"  //   , // degrees
45378
45379          // "&#233;"     , // e ecute
45380          // "&#250;"     , // u ecute?
45381     ],
45382     
45383     specialElements : [
45384         {
45385             text: "Insert Table",
45386             xtype: 'MenuItem',
45387             xns : Roo.Menu,
45388             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45389                 
45390         },
45391         {    
45392             text: "Insert Image",
45393             xtype: 'MenuItem',
45394             xns : Roo.Menu,
45395             ihtml : '<img src="about:blank"/>'
45396             
45397         }
45398         
45399          
45400     ],
45401     
45402     
45403     inputElements : [ 
45404             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45405             "input:submit", "input:button", "select", "textarea", "label" ],
45406     formats : [
45407         ["p"] ,  
45408         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45409         ["pre"],[ "code"], 
45410         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45411         ['div'],['span'],
45412         ['sup'],['sub']
45413     ],
45414     
45415     cleanStyles : [
45416         "font-size"
45417     ],
45418      /**
45419      * @cfg {String} defaultFont default font to use.
45420      */
45421     defaultFont: 'tahoma',
45422    
45423     fontSelect : false,
45424     
45425     
45426     formatCombo : false,
45427     
45428     init : function(editor)
45429     {
45430         this.editor = editor;
45431         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45432         var editorcore = this.editorcore;
45433         
45434         var _t = this;
45435         
45436         var fid = editorcore.frameId;
45437         var etb = this;
45438         function btn(id, toggle, handler){
45439             var xid = fid + '-'+ id ;
45440             return {
45441                 id : xid,
45442                 cmd : id,
45443                 cls : 'x-btn-icon x-edit-'+id,
45444                 enableToggle:toggle !== false,
45445                 scope: _t, // was editor...
45446                 handler:handler||_t.relayBtnCmd,
45447                 clickEvent:'mousedown',
45448                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45449                 tabIndex:-1
45450             };
45451         }
45452         
45453         
45454         
45455         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45456         this.tb = tb;
45457          // stop form submits
45458         tb.el.on('click', function(e){
45459             e.preventDefault(); // what does this do?
45460         });
45461
45462         if(!this.disable.font) { // && !Roo.isSafari){
45463             /* why no safari for fonts 
45464             editor.fontSelect = tb.el.createChild({
45465                 tag:'select',
45466                 tabIndex: -1,
45467                 cls:'x-font-select',
45468                 html: this.createFontOptions()
45469             });
45470             
45471             editor.fontSelect.on('change', function(){
45472                 var font = editor.fontSelect.dom.value;
45473                 editor.relayCmd('fontname', font);
45474                 editor.deferFocus();
45475             }, editor);
45476             
45477             tb.add(
45478                 editor.fontSelect.dom,
45479                 '-'
45480             );
45481             */
45482             
45483         };
45484         if(!this.disable.formats){
45485             this.formatCombo = new Roo.form.ComboBox({
45486                 store: new Roo.data.SimpleStore({
45487                     id : 'tag',
45488                     fields: ['tag'],
45489                     data : this.formats // from states.js
45490                 }),
45491                 blockFocus : true,
45492                 name : '',
45493                 //autoCreate : {tag: "div",  size: "20"},
45494                 displayField:'tag',
45495                 typeAhead: false,
45496                 mode: 'local',
45497                 editable : false,
45498                 triggerAction: 'all',
45499                 emptyText:'Add tag',
45500                 selectOnFocus:true,
45501                 width:135,
45502                 listeners : {
45503                     'select': function(c, r, i) {
45504                         editorcore.insertTag(r.get('tag'));
45505                         editor.focus();
45506                     }
45507                 }
45508
45509             });
45510             tb.addField(this.formatCombo);
45511             
45512         }
45513         
45514         if(!this.disable.format){
45515             tb.add(
45516                 btn('bold'),
45517                 btn('italic'),
45518                 btn('underline'),
45519                 btn('strikethrough')
45520             );
45521         };
45522         if(!this.disable.fontSize){
45523             tb.add(
45524                 '-',
45525                 
45526                 
45527                 btn('increasefontsize', false, editorcore.adjustFont),
45528                 btn('decreasefontsize', false, editorcore.adjustFont)
45529             );
45530         };
45531         
45532         
45533         if(!this.disable.colors){
45534             tb.add(
45535                 '-', {
45536                     id:editorcore.frameId +'-forecolor',
45537                     cls:'x-btn-icon x-edit-forecolor',
45538                     clickEvent:'mousedown',
45539                     tooltip: this.buttonTips['forecolor'] || undefined,
45540                     tabIndex:-1,
45541                     menu : new Roo.menu.ColorMenu({
45542                         allowReselect: true,
45543                         focus: Roo.emptyFn,
45544                         value:'000000',
45545                         plain:true,
45546                         selectHandler: function(cp, color){
45547                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45548                             editor.deferFocus();
45549                         },
45550                         scope: editorcore,
45551                         clickEvent:'mousedown'
45552                     })
45553                 }, {
45554                     id:editorcore.frameId +'backcolor',
45555                     cls:'x-btn-icon x-edit-backcolor',
45556                     clickEvent:'mousedown',
45557                     tooltip: this.buttonTips['backcolor'] || undefined,
45558                     tabIndex:-1,
45559                     menu : new Roo.menu.ColorMenu({
45560                         focus: Roo.emptyFn,
45561                         value:'FFFFFF',
45562                         plain:true,
45563                         allowReselect: true,
45564                         selectHandler: function(cp, color){
45565                             if(Roo.isGecko){
45566                                 editorcore.execCmd('useCSS', false);
45567                                 editorcore.execCmd('hilitecolor', color);
45568                                 editorcore.execCmd('useCSS', true);
45569                                 editor.deferFocus();
45570                             }else{
45571                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45572                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45573                                 editor.deferFocus();
45574                             }
45575                         },
45576                         scope:editorcore,
45577                         clickEvent:'mousedown'
45578                     })
45579                 }
45580             );
45581         };
45582         // now add all the items...
45583         
45584
45585         if(!this.disable.alignments){
45586             tb.add(
45587                 '-',
45588                 btn('justifyleft'),
45589                 btn('justifycenter'),
45590                 btn('justifyright')
45591             );
45592         };
45593
45594         //if(!Roo.isSafari){
45595             if(!this.disable.links){
45596                 tb.add(
45597                     '-',
45598                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45599                 );
45600             };
45601
45602             if(!this.disable.lists){
45603                 tb.add(
45604                     '-',
45605                     btn('insertorderedlist'),
45606                     btn('insertunorderedlist')
45607                 );
45608             }
45609             if(!this.disable.sourceEdit){
45610                 tb.add(
45611                     '-',
45612                     btn('sourceedit', true, function(btn){
45613                         this.toggleSourceEdit(btn.pressed);
45614                     })
45615                 );
45616             }
45617         //}
45618         
45619         var smenu = { };
45620         // special menu.. - needs to be tidied up..
45621         if (!this.disable.special) {
45622             smenu = {
45623                 text: "&#169;",
45624                 cls: 'x-edit-none',
45625                 
45626                 menu : {
45627                     items : []
45628                 }
45629             };
45630             for (var i =0; i < this.specialChars.length; i++) {
45631                 smenu.menu.items.push({
45632                     
45633                     html: this.specialChars[i],
45634                     handler: function(a,b) {
45635                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45636                         //editor.insertAtCursor(a.html);
45637                         
45638                     },
45639                     tabIndex:-1
45640                 });
45641             }
45642             
45643             
45644             tb.add(smenu);
45645             
45646             
45647         }
45648         
45649         var cmenu = { };
45650         if (!this.disable.cleanStyles) {
45651             cmenu = {
45652                 cls: 'x-btn-icon x-btn-clear',
45653                 
45654                 menu : {
45655                     items : []
45656                 }
45657             };
45658             for (var i =0; i < this.cleanStyles.length; i++) {
45659                 cmenu.menu.items.push({
45660                     actiontype : this.cleanStyles[i],
45661                     html: 'Remove ' + this.cleanStyles[i],
45662                     handler: function(a,b) {
45663 //                        Roo.log(a);
45664 //                        Roo.log(b);
45665                         var c = Roo.get(editorcore.doc.body);
45666                         c.select('[style]').each(function(s) {
45667                             s.dom.style.removeProperty(a.actiontype);
45668                         });
45669                         editorcore.syncValue();
45670                     },
45671                     tabIndex:-1
45672                 });
45673             }
45674              cmenu.menu.items.push({
45675                 actiontype : 'tablewidths',
45676                 html: 'Remove Table Widths',
45677                 handler: function(a,b) {
45678                     editorcore.cleanTableWidths();
45679                     editorcore.syncValue();
45680                 },
45681                 tabIndex:-1
45682             });
45683             cmenu.menu.items.push({
45684                 actiontype : 'word',
45685                 html: 'Remove MS Word Formating',
45686                 handler: function(a,b) {
45687                     editorcore.cleanWord();
45688                     editorcore.syncValue();
45689                 },
45690                 tabIndex:-1
45691             });
45692             
45693             cmenu.menu.items.push({
45694                 actiontype : 'all',
45695                 html: 'Remove All Styles',
45696                 handler: function(a,b) {
45697                     
45698                     var c = Roo.get(editorcore.doc.body);
45699                     c.select('[style]').each(function(s) {
45700                         s.dom.removeAttribute('style');
45701                     });
45702                     editorcore.syncValue();
45703                 },
45704                 tabIndex:-1
45705             });
45706             
45707             cmenu.menu.items.push({
45708                 actiontype : 'all',
45709                 html: 'Remove All CSS Classes',
45710                 handler: function(a,b) {
45711                     
45712                     var c = Roo.get(editorcore.doc.body);
45713                     c.select('[class]').each(function(s) {
45714                         s.dom.removeAttribute('class');
45715                     });
45716                     editorcore.cleanWord();
45717                     editorcore.syncValue();
45718                 },
45719                 tabIndex:-1
45720             });
45721             
45722              cmenu.menu.items.push({
45723                 actiontype : 'tidy',
45724                 html: 'Tidy HTML Source',
45725                 handler: function(a,b) {
45726                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45727                     editorcore.syncValue();
45728                 },
45729                 tabIndex:-1
45730             });
45731             
45732             
45733             tb.add(cmenu);
45734         }
45735          
45736         if (!this.disable.specialElements) {
45737             var semenu = {
45738                 text: "Other;",
45739                 cls: 'x-edit-none',
45740                 menu : {
45741                     items : []
45742                 }
45743             };
45744             for (var i =0; i < this.specialElements.length; i++) {
45745                 semenu.menu.items.push(
45746                     Roo.apply({ 
45747                         handler: function(a,b) {
45748                             editor.insertAtCursor(this.ihtml);
45749                         }
45750                     }, this.specialElements[i])
45751                 );
45752                     
45753             }
45754             
45755             tb.add(semenu);
45756             
45757             
45758         }
45759          
45760         
45761         if (this.btns) {
45762             for(var i =0; i< this.btns.length;i++) {
45763                 var b = Roo.factory(this.btns[i],Roo.form);
45764                 b.cls =  'x-edit-none';
45765                 
45766                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45767                     b.cls += ' x-init-enable';
45768                 }
45769                 
45770                 b.scope = editorcore;
45771                 tb.add(b);
45772             }
45773         
45774         }
45775         
45776         
45777         
45778         // disable everything...
45779         
45780         this.tb.items.each(function(item){
45781             
45782            if(
45783                 item.id != editorcore.frameId+ '-sourceedit' && 
45784                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45785             ){
45786                 
45787                 item.disable();
45788             }
45789         });
45790         this.rendered = true;
45791         
45792         // the all the btns;
45793         editor.on('editorevent', this.updateToolbar, this);
45794         // other toolbars need to implement this..
45795         //editor.on('editmodechange', this.updateToolbar, this);
45796     },
45797     
45798     
45799     relayBtnCmd : function(btn) {
45800         this.editorcore.relayCmd(btn.cmd);
45801     },
45802     // private used internally
45803     createLink : function(){
45804         Roo.log("create link?");
45805         var url = prompt(this.createLinkText, this.defaultLinkValue);
45806         if(url && url != 'http:/'+'/'){
45807             this.editorcore.relayCmd('createlink', url);
45808         }
45809     },
45810
45811     
45812     /**
45813      * Protected method that will not generally be called directly. It triggers
45814      * a toolbar update by reading the markup state of the current selection in the editor.
45815      */
45816     updateToolbar: function(){
45817
45818         if(!this.editorcore.activated){
45819             this.editor.onFirstFocus();
45820             return;
45821         }
45822
45823         var btns = this.tb.items.map, 
45824             doc = this.editorcore.doc,
45825             frameId = this.editorcore.frameId;
45826
45827         if(!this.disable.font && !Roo.isSafari){
45828             /*
45829             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45830             if(name != this.fontSelect.dom.value){
45831                 this.fontSelect.dom.value = name;
45832             }
45833             */
45834         }
45835         if(!this.disable.format){
45836             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45837             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45838             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45839             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45840         }
45841         if(!this.disable.alignments){
45842             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45843             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45844             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45845         }
45846         if(!Roo.isSafari && !this.disable.lists){
45847             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45848             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45849         }
45850         
45851         var ans = this.editorcore.getAllAncestors();
45852         if (this.formatCombo) {
45853             
45854             
45855             var store = this.formatCombo.store;
45856             this.formatCombo.setValue("");
45857             for (var i =0; i < ans.length;i++) {
45858                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45859                     // select it..
45860                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45861                     break;
45862                 }
45863             }
45864         }
45865         
45866         
45867         
45868         // hides menus... - so this cant be on a menu...
45869         Roo.menu.MenuMgr.hideAll();
45870
45871         //this.editorsyncValue();
45872     },
45873    
45874     
45875     createFontOptions : function(){
45876         var buf = [], fs = this.fontFamilies, ff, lc;
45877         
45878         
45879         
45880         for(var i = 0, len = fs.length; i< len; i++){
45881             ff = fs[i];
45882             lc = ff.toLowerCase();
45883             buf.push(
45884                 '<option value="',lc,'" style="font-family:',ff,';"',
45885                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45886                     ff,
45887                 '</option>'
45888             );
45889         }
45890         return buf.join('');
45891     },
45892     
45893     toggleSourceEdit : function(sourceEditMode){
45894         
45895         Roo.log("toolbar toogle");
45896         if(sourceEditMode === undefined){
45897             sourceEditMode = !this.sourceEditMode;
45898         }
45899         this.sourceEditMode = sourceEditMode === true;
45900         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45901         // just toggle the button?
45902         if(btn.pressed !== this.sourceEditMode){
45903             btn.toggle(this.sourceEditMode);
45904             return;
45905         }
45906         
45907         if(sourceEditMode){
45908             Roo.log("disabling buttons");
45909             this.tb.items.each(function(item){
45910                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45911                     item.disable();
45912                 }
45913             });
45914           
45915         }else{
45916             Roo.log("enabling buttons");
45917             if(this.editorcore.initialized){
45918                 this.tb.items.each(function(item){
45919                     item.enable();
45920                 });
45921             }
45922             
45923         }
45924         Roo.log("calling toggole on editor");
45925         // tell the editor that it's been pressed..
45926         this.editor.toggleSourceEdit(sourceEditMode);
45927        
45928     },
45929      /**
45930      * Object collection of toolbar tooltips for the buttons in the editor. The key
45931      * is the command id associated with that button and the value is a valid QuickTips object.
45932      * For example:
45933 <pre><code>
45934 {
45935     bold : {
45936         title: 'Bold (Ctrl+B)',
45937         text: 'Make the selected text bold.',
45938         cls: 'x-html-editor-tip'
45939     },
45940     italic : {
45941         title: 'Italic (Ctrl+I)',
45942         text: 'Make the selected text italic.',
45943         cls: 'x-html-editor-tip'
45944     },
45945     ...
45946 </code></pre>
45947     * @type Object
45948      */
45949     buttonTips : {
45950         bold : {
45951             title: 'Bold (Ctrl+B)',
45952             text: 'Make the selected text bold.',
45953             cls: 'x-html-editor-tip'
45954         },
45955         italic : {
45956             title: 'Italic (Ctrl+I)',
45957             text: 'Make the selected text italic.',
45958             cls: 'x-html-editor-tip'
45959         },
45960         underline : {
45961             title: 'Underline (Ctrl+U)',
45962             text: 'Underline the selected text.',
45963             cls: 'x-html-editor-tip'
45964         },
45965         strikethrough : {
45966             title: 'Strikethrough',
45967             text: 'Strikethrough the selected text.',
45968             cls: 'x-html-editor-tip'
45969         },
45970         increasefontsize : {
45971             title: 'Grow Text',
45972             text: 'Increase the font size.',
45973             cls: 'x-html-editor-tip'
45974         },
45975         decreasefontsize : {
45976             title: 'Shrink Text',
45977             text: 'Decrease the font size.',
45978             cls: 'x-html-editor-tip'
45979         },
45980         backcolor : {
45981             title: 'Text Highlight Color',
45982             text: 'Change the background color of the selected text.',
45983             cls: 'x-html-editor-tip'
45984         },
45985         forecolor : {
45986             title: 'Font Color',
45987             text: 'Change the color of the selected text.',
45988             cls: 'x-html-editor-tip'
45989         },
45990         justifyleft : {
45991             title: 'Align Text Left',
45992             text: 'Align text to the left.',
45993             cls: 'x-html-editor-tip'
45994         },
45995         justifycenter : {
45996             title: 'Center Text',
45997             text: 'Center text in the editor.',
45998             cls: 'x-html-editor-tip'
45999         },
46000         justifyright : {
46001             title: 'Align Text Right',
46002             text: 'Align text to the right.',
46003             cls: 'x-html-editor-tip'
46004         },
46005         insertunorderedlist : {
46006             title: 'Bullet List',
46007             text: 'Start a bulleted list.',
46008             cls: 'x-html-editor-tip'
46009         },
46010         insertorderedlist : {
46011             title: 'Numbered List',
46012             text: 'Start a numbered list.',
46013             cls: 'x-html-editor-tip'
46014         },
46015         createlink : {
46016             title: 'Hyperlink',
46017             text: 'Make the selected text a hyperlink.',
46018             cls: 'x-html-editor-tip'
46019         },
46020         sourceedit : {
46021             title: 'Source Edit',
46022             text: 'Switch to source editing mode.',
46023             cls: 'x-html-editor-tip'
46024         }
46025     },
46026     // private
46027     onDestroy : function(){
46028         if(this.rendered){
46029             
46030             this.tb.items.each(function(item){
46031                 if(item.menu){
46032                     item.menu.removeAll();
46033                     if(item.menu.el){
46034                         item.menu.el.destroy();
46035                     }
46036                 }
46037                 item.destroy();
46038             });
46039              
46040         }
46041     },
46042     onFirstFocus: function() {
46043         this.tb.items.each(function(item){
46044            item.enable();
46045         });
46046     }
46047 });
46048
46049
46050
46051
46052 // <script type="text/javascript">
46053 /*
46054  * Based on
46055  * Ext JS Library 1.1.1
46056  * Copyright(c) 2006-2007, Ext JS, LLC.
46057  *  
46058  
46059  */
46060
46061  
46062 /**
46063  * @class Roo.form.HtmlEditor.ToolbarContext
46064  * Context Toolbar
46065  * 
46066  * Usage:
46067  *
46068  new Roo.form.HtmlEditor({
46069     ....
46070     toolbars : [
46071         { xtype: 'ToolbarStandard', styles : {} }
46072         { xtype: 'ToolbarContext', disable : {} }
46073     ]
46074 })
46075
46076      
46077  * 
46078  * @config : {Object} disable List of elements to disable.. (not done yet.)
46079  * @config : {Object} styles  Map of styles available.
46080  * 
46081  */
46082
46083 Roo.form.HtmlEditor.ToolbarContext = function(config)
46084 {
46085     
46086     Roo.apply(this, config);
46087     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46088     // dont call parent... till later.
46089     this.styles = this.styles || {};
46090 }
46091
46092  
46093
46094 Roo.form.HtmlEditor.ToolbarContext.types = {
46095     'IMG' : {
46096         width : {
46097             title: "Width",
46098             width: 40
46099         },
46100         height:  {
46101             title: "Height",
46102             width: 40
46103         },
46104         align: {
46105             title: "Align",
46106             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46107             width : 80
46108             
46109         },
46110         border: {
46111             title: "Border",
46112             width: 40
46113         },
46114         alt: {
46115             title: "Alt",
46116             width: 120
46117         },
46118         src : {
46119             title: "Src",
46120             width: 220
46121         }
46122         
46123     },
46124     'A' : {
46125         name : {
46126             title: "Name",
46127             width: 50
46128         },
46129         target:  {
46130             title: "Target",
46131             width: 120
46132         },
46133         href:  {
46134             title: "Href",
46135             width: 220
46136         } // border?
46137         
46138     },
46139     'TABLE' : {
46140         rows : {
46141             title: "Rows",
46142             width: 20
46143         },
46144         cols : {
46145             title: "Cols",
46146             width: 20
46147         },
46148         width : {
46149             title: "Width",
46150             width: 40
46151         },
46152         height : {
46153             title: "Height",
46154             width: 40
46155         },
46156         border : {
46157             title: "Border",
46158             width: 20
46159         }
46160     },
46161     'TD' : {
46162         width : {
46163             title: "Width",
46164             width: 40
46165         },
46166         height : {
46167             title: "Height",
46168             width: 40
46169         },   
46170         align: {
46171             title: "Align",
46172             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46173             width: 80
46174         },
46175         valign: {
46176             title: "Valign",
46177             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46178             width: 80
46179         },
46180         colspan: {
46181             title: "Colspan",
46182             width: 20
46183             
46184         },
46185          'font-family'  : {
46186             title : "Font",
46187             style : 'fontFamily',
46188             displayField: 'display',
46189             optname : 'font-family',
46190             width: 140
46191         }
46192     },
46193     'INPUT' : {
46194         name : {
46195             title: "name",
46196             width: 120
46197         },
46198         value : {
46199             title: "Value",
46200             width: 120
46201         },
46202         width : {
46203             title: "Width",
46204             width: 40
46205         }
46206     },
46207     'LABEL' : {
46208         'for' : {
46209             title: "For",
46210             width: 120
46211         }
46212     },
46213     'TEXTAREA' : {
46214           name : {
46215             title: "name",
46216             width: 120
46217         },
46218         rows : {
46219             title: "Rows",
46220             width: 20
46221         },
46222         cols : {
46223             title: "Cols",
46224             width: 20
46225         }
46226     },
46227     'SELECT' : {
46228         name : {
46229             title: "name",
46230             width: 120
46231         },
46232         selectoptions : {
46233             title: "Options",
46234             width: 200
46235         }
46236     },
46237     
46238     // should we really allow this??
46239     // should this just be 
46240     'BODY' : {
46241         title : {
46242             title: "Title",
46243             width: 200,
46244             disabled : true
46245         }
46246     },
46247     'SPAN' : {
46248         'font-family'  : {
46249             title : "Font",
46250             style : 'fontFamily',
46251             displayField: 'display',
46252             optname : 'font-family',
46253             width: 140
46254         }
46255     },
46256     'DIV' : {
46257         'font-family'  : {
46258             title : "Font",
46259             style : 'fontFamily',
46260             displayField: 'display',
46261             optname : 'font-family',
46262             width: 140
46263         }
46264     },
46265      'P' : {
46266         'font-family'  : {
46267             title : "Font",
46268             style : 'fontFamily',
46269             displayField: 'display',
46270             optname : 'font-family',
46271             width: 140
46272         }
46273     },
46274     
46275     '*' : {
46276         // empty..
46277     }
46278
46279 };
46280
46281 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46282 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46283
46284 Roo.form.HtmlEditor.ToolbarContext.options = {
46285         'font-family'  : [ 
46286                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46287                 [ 'Courier New', 'Courier New'],
46288                 [ 'Tahoma', 'Tahoma'],
46289                 [ 'Times New Roman,serif', 'Times'],
46290                 [ 'Verdana','Verdana' ]
46291         ]
46292 };
46293
46294 // fixme - these need to be configurable..
46295  
46296
46297 //Roo.form.HtmlEditor.ToolbarContext.types
46298
46299
46300 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46301     
46302     tb: false,
46303     
46304     rendered: false,
46305     
46306     editor : false,
46307     editorcore : false,
46308     /**
46309      * @cfg {Object} disable  List of toolbar elements to disable
46310          
46311      */
46312     disable : false,
46313     /**
46314      * @cfg {Object} styles List of styles 
46315      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46316      *
46317      * These must be defined in the page, so they get rendered correctly..
46318      * .headline { }
46319      * TD.underline { }
46320      * 
46321      */
46322     styles : false,
46323     
46324     options: false,
46325     
46326     toolbars : false,
46327     
46328     init : function(editor)
46329     {
46330         this.editor = editor;
46331         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46332         var editorcore = this.editorcore;
46333         
46334         var fid = editorcore.frameId;
46335         var etb = this;
46336         function btn(id, toggle, handler){
46337             var xid = fid + '-'+ id ;
46338             return {
46339                 id : xid,
46340                 cmd : id,
46341                 cls : 'x-btn-icon x-edit-'+id,
46342                 enableToggle:toggle !== false,
46343                 scope: editorcore, // was editor...
46344                 handler:handler||editorcore.relayBtnCmd,
46345                 clickEvent:'mousedown',
46346                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46347                 tabIndex:-1
46348             };
46349         }
46350         // create a new element.
46351         var wdiv = editor.wrap.createChild({
46352                 tag: 'div'
46353             }, editor.wrap.dom.firstChild.nextSibling, true);
46354         
46355         // can we do this more than once??
46356         
46357          // stop form submits
46358       
46359  
46360         // disable everything...
46361         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46362         this.toolbars = {};
46363            
46364         for (var i in  ty) {
46365           
46366             this.toolbars[i] = this.buildToolbar(ty[i],i);
46367         }
46368         this.tb = this.toolbars.BODY;
46369         this.tb.el.show();
46370         this.buildFooter();
46371         this.footer.show();
46372         editor.on('hide', function( ) { this.footer.hide() }, this);
46373         editor.on('show', function( ) { this.footer.show() }, this);
46374         
46375          
46376         this.rendered = true;
46377         
46378         // the all the btns;
46379         editor.on('editorevent', this.updateToolbar, this);
46380         // other toolbars need to implement this..
46381         //editor.on('editmodechange', this.updateToolbar, this);
46382     },
46383     
46384     
46385     
46386     /**
46387      * Protected method that will not generally be called directly. It triggers
46388      * a toolbar update by reading the markup state of the current selection in the editor.
46389      *
46390      * Note you can force an update by calling on('editorevent', scope, false)
46391      */
46392     updateToolbar: function(editor,ev,sel){
46393
46394         //Roo.log(ev);
46395         // capture mouse up - this is handy for selecting images..
46396         // perhaps should go somewhere else...
46397         if(!this.editorcore.activated){
46398              this.editor.onFirstFocus();
46399             return;
46400         }
46401         
46402         
46403         
46404         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46405         // selectNode - might want to handle IE?
46406         if (ev &&
46407             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46408             ev.target && ev.target.tagName == 'IMG') {
46409             // they have click on an image...
46410             // let's see if we can change the selection...
46411             sel = ev.target;
46412          
46413               var nodeRange = sel.ownerDocument.createRange();
46414             try {
46415                 nodeRange.selectNode(sel);
46416             } catch (e) {
46417                 nodeRange.selectNodeContents(sel);
46418             }
46419             //nodeRange.collapse(true);
46420             var s = this.editorcore.win.getSelection();
46421             s.removeAllRanges();
46422             s.addRange(nodeRange);
46423         }  
46424         
46425       
46426         var updateFooter = sel ? false : true;
46427         
46428         
46429         var ans = this.editorcore.getAllAncestors();
46430         
46431         // pick
46432         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46433         
46434         if (!sel) { 
46435             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46436             sel = sel ? sel : this.editorcore.doc.body;
46437             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46438             
46439         }
46440         // pick a menu that exists..
46441         var tn = sel.tagName.toUpperCase();
46442         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46443         
46444         tn = sel.tagName.toUpperCase();
46445         
46446         var lastSel = this.tb.selectedNode;
46447         
46448         this.tb.selectedNode = sel;
46449         
46450         // if current menu does not match..
46451         
46452         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46453                 
46454             this.tb.el.hide();
46455             ///console.log("show: " + tn);
46456             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46457             this.tb.el.show();
46458             // update name
46459             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46460             
46461             
46462             // update attributes
46463             if (this.tb.fields) {
46464                 this.tb.fields.each(function(e) {
46465                     if (e.stylename) {
46466                         e.setValue(sel.style[e.stylename]);
46467                         return;
46468                     } 
46469                    e.setValue(sel.getAttribute(e.attrname));
46470                 });
46471             }
46472             
46473             var hasStyles = false;
46474             for(var i in this.styles) {
46475                 hasStyles = true;
46476                 break;
46477             }
46478             
46479             // update styles
46480             if (hasStyles) { 
46481                 var st = this.tb.fields.item(0);
46482                 
46483                 st.store.removeAll();
46484                
46485                 
46486                 var cn = sel.className.split(/\s+/);
46487                 
46488                 var avs = [];
46489                 if (this.styles['*']) {
46490                     
46491                     Roo.each(this.styles['*'], function(v) {
46492                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46493                     });
46494                 }
46495                 if (this.styles[tn]) { 
46496                     Roo.each(this.styles[tn], function(v) {
46497                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46498                     });
46499                 }
46500                 
46501                 st.store.loadData(avs);
46502                 st.collapse();
46503                 st.setValue(cn);
46504             }
46505             // flag our selected Node.
46506             this.tb.selectedNode = sel;
46507            
46508            
46509             Roo.menu.MenuMgr.hideAll();
46510
46511         }
46512         
46513         if (!updateFooter) {
46514             //this.footDisp.dom.innerHTML = ''; 
46515             return;
46516         }
46517         // update the footer
46518         //
46519         var html = '';
46520         
46521         this.footerEls = ans.reverse();
46522         Roo.each(this.footerEls, function(a,i) {
46523             if (!a) { return; }
46524             html += html.length ? ' &gt; '  :  '';
46525             
46526             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46527             
46528         });
46529        
46530         // 
46531         var sz = this.footDisp.up('td').getSize();
46532         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46533         this.footDisp.dom.style.marginLeft = '5px';
46534         
46535         this.footDisp.dom.style.overflow = 'hidden';
46536         
46537         this.footDisp.dom.innerHTML = html;
46538             
46539         //this.editorsyncValue();
46540     },
46541      
46542     
46543    
46544        
46545     // private
46546     onDestroy : function(){
46547         if(this.rendered){
46548             
46549             this.tb.items.each(function(item){
46550                 if(item.menu){
46551                     item.menu.removeAll();
46552                     if(item.menu.el){
46553                         item.menu.el.destroy();
46554                     }
46555                 }
46556                 item.destroy();
46557             });
46558              
46559         }
46560     },
46561     onFirstFocus: function() {
46562         // need to do this for all the toolbars..
46563         this.tb.items.each(function(item){
46564            item.enable();
46565         });
46566     },
46567     buildToolbar: function(tlist, nm)
46568     {
46569         var editor = this.editor;
46570         var editorcore = this.editorcore;
46571          // create a new element.
46572         var wdiv = editor.wrap.createChild({
46573                 tag: 'div'
46574             }, editor.wrap.dom.firstChild.nextSibling, true);
46575         
46576        
46577         var tb = new Roo.Toolbar(wdiv);
46578         // add the name..
46579         
46580         tb.add(nm+ ":&nbsp;");
46581         
46582         var styles = [];
46583         for(var i in this.styles) {
46584             styles.push(i);
46585         }
46586         
46587         // styles...
46588         if (styles && styles.length) {
46589             
46590             // this needs a multi-select checkbox...
46591             tb.addField( new Roo.form.ComboBox({
46592                 store: new Roo.data.SimpleStore({
46593                     id : 'val',
46594                     fields: ['val', 'selected'],
46595                     data : [] 
46596                 }),
46597                 name : '-roo-edit-className',
46598                 attrname : 'className',
46599                 displayField: 'val',
46600                 typeAhead: false,
46601                 mode: 'local',
46602                 editable : false,
46603                 triggerAction: 'all',
46604                 emptyText:'Select Style',
46605                 selectOnFocus:true,
46606                 width: 130,
46607                 listeners : {
46608                     'select': function(c, r, i) {
46609                         // initial support only for on class per el..
46610                         tb.selectedNode.className =  r ? r.get('val') : '';
46611                         editorcore.syncValue();
46612                     }
46613                 }
46614     
46615             }));
46616         }
46617         
46618         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46619         var tbops = tbc.options;
46620         
46621         for (var i in tlist) {
46622             
46623             var item = tlist[i];
46624             tb.add(item.title + ":&nbsp;");
46625             
46626             
46627             //optname == used so you can configure the options available..
46628             var opts = item.opts ? item.opts : false;
46629             if (item.optname) {
46630                 opts = tbops[item.optname];
46631            
46632             }
46633             
46634             if (opts) {
46635                 // opts == pulldown..
46636                 tb.addField( new Roo.form.ComboBox({
46637                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46638                         id : 'val',
46639                         fields: ['val', 'display'],
46640                         data : opts  
46641                     }),
46642                     name : '-roo-edit-' + i,
46643                     attrname : i,
46644                     stylename : item.style ? item.style : false,
46645                     displayField: item.displayField ? item.displayField : 'val',
46646                     valueField :  'val',
46647                     typeAhead: false,
46648                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46649                     editable : false,
46650                     triggerAction: 'all',
46651                     emptyText:'Select',
46652                     selectOnFocus:true,
46653                     width: item.width ? item.width  : 130,
46654                     listeners : {
46655                         'select': function(c, r, i) {
46656                             if (c.stylename) {
46657                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46658                                 return;
46659                             }
46660                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46661                         }
46662                     }
46663
46664                 }));
46665                 continue;
46666                     
46667                  
46668                 
46669                 tb.addField( new Roo.form.TextField({
46670                     name: i,
46671                     width: 100,
46672                     //allowBlank:false,
46673                     value: ''
46674                 }));
46675                 continue;
46676             }
46677             tb.addField( new Roo.form.TextField({
46678                 name: '-roo-edit-' + i,
46679                 attrname : i,
46680                 
46681                 width: item.width,
46682                 //allowBlank:true,
46683                 value: '',
46684                 listeners: {
46685                     'change' : function(f, nv, ov) {
46686                         tb.selectedNode.setAttribute(f.attrname, nv);
46687                         editorcore.syncValue();
46688                     }
46689                 }
46690             }));
46691              
46692         }
46693         
46694         var _this = this;
46695         
46696         if(nm == 'BODY'){
46697             tb.addSeparator();
46698         
46699             tb.addButton( {
46700                 text: 'Stylesheets',
46701
46702                 listeners : {
46703                     click : function ()
46704                     {
46705                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46706                     }
46707                 }
46708             });
46709         }
46710         
46711         tb.addFill();
46712         tb.addButton( {
46713             text: 'Remove Tag',
46714     
46715             listeners : {
46716                 click : function ()
46717                 {
46718                     // remove
46719                     // undo does not work.
46720                      
46721                     var sn = tb.selectedNode;
46722                     
46723                     var pn = sn.parentNode;
46724                     
46725                     var stn =  sn.childNodes[0];
46726                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46727                     while (sn.childNodes.length) {
46728                         var node = sn.childNodes[0];
46729                         sn.removeChild(node);
46730                         //Roo.log(node);
46731                         pn.insertBefore(node, sn);
46732                         
46733                     }
46734                     pn.removeChild(sn);
46735                     var range = editorcore.createRange();
46736         
46737                     range.setStart(stn,0);
46738                     range.setEnd(en,0); //????
46739                     //range.selectNode(sel);
46740                     
46741                     
46742                     var selection = editorcore.getSelection();
46743                     selection.removeAllRanges();
46744                     selection.addRange(range);
46745                     
46746                     
46747                     
46748                     //_this.updateToolbar(null, null, pn);
46749                     _this.updateToolbar(null, null, null);
46750                     _this.footDisp.dom.innerHTML = ''; 
46751                 }
46752             }
46753             
46754                     
46755                 
46756             
46757         });
46758         
46759         
46760         tb.el.on('click', function(e){
46761             e.preventDefault(); // what does this do?
46762         });
46763         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46764         tb.el.hide();
46765         tb.name = nm;
46766         // dont need to disable them... as they will get hidden
46767         return tb;
46768          
46769         
46770     },
46771     buildFooter : function()
46772     {
46773         
46774         var fel = this.editor.wrap.createChild();
46775         this.footer = new Roo.Toolbar(fel);
46776         // toolbar has scrolly on left / right?
46777         var footDisp= new Roo.Toolbar.Fill();
46778         var _t = this;
46779         this.footer.add(
46780             {
46781                 text : '&lt;',
46782                 xtype: 'Button',
46783                 handler : function() {
46784                     _t.footDisp.scrollTo('left',0,true)
46785                 }
46786             }
46787         );
46788         this.footer.add( footDisp );
46789         this.footer.add( 
46790             {
46791                 text : '&gt;',
46792                 xtype: 'Button',
46793                 handler : function() {
46794                     // no animation..
46795                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46796                 }
46797             }
46798         );
46799         var fel = Roo.get(footDisp.el);
46800         fel.addClass('x-editor-context');
46801         this.footDispWrap = fel; 
46802         this.footDispWrap.overflow  = 'hidden';
46803         
46804         this.footDisp = fel.createChild();
46805         this.footDispWrap.on('click', this.onContextClick, this)
46806         
46807         
46808     },
46809     onContextClick : function (ev,dom)
46810     {
46811         ev.preventDefault();
46812         var  cn = dom.className;
46813         //Roo.log(cn);
46814         if (!cn.match(/x-ed-loc-/)) {
46815             return;
46816         }
46817         var n = cn.split('-').pop();
46818         var ans = this.footerEls;
46819         var sel = ans[n];
46820         
46821          // pick
46822         var range = this.editorcore.createRange();
46823         
46824         range.selectNodeContents(sel);
46825         //range.selectNode(sel);
46826         
46827         
46828         var selection = this.editorcore.getSelection();
46829         selection.removeAllRanges();
46830         selection.addRange(range);
46831         
46832         
46833         
46834         this.updateToolbar(null, null, sel);
46835         
46836         
46837     }
46838     
46839     
46840     
46841     
46842     
46843 });
46844
46845
46846
46847
46848
46849 /*
46850  * Based on:
46851  * Ext JS Library 1.1.1
46852  * Copyright(c) 2006-2007, Ext JS, LLC.
46853  *
46854  * Originally Released Under LGPL - original licence link has changed is not relivant.
46855  *
46856  * Fork - LGPL
46857  * <script type="text/javascript">
46858  */
46859  
46860 /**
46861  * @class Roo.form.BasicForm
46862  * @extends Roo.util.Observable
46863  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46864  * @constructor
46865  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46866  * @param {Object} config Configuration options
46867  */
46868 Roo.form.BasicForm = function(el, config){
46869     this.allItems = [];
46870     this.childForms = [];
46871     Roo.apply(this, config);
46872     /*
46873      * The Roo.form.Field items in this form.
46874      * @type MixedCollection
46875      */
46876      
46877      
46878     this.items = new Roo.util.MixedCollection(false, function(o){
46879         return o.id || (o.id = Roo.id());
46880     });
46881     this.addEvents({
46882         /**
46883          * @event beforeaction
46884          * Fires before any action is performed. Return false to cancel the action.
46885          * @param {Form} this
46886          * @param {Action} action The action to be performed
46887          */
46888         beforeaction: true,
46889         /**
46890          * @event actionfailed
46891          * Fires when an action fails.
46892          * @param {Form} this
46893          * @param {Action} action The action that failed
46894          */
46895         actionfailed : true,
46896         /**
46897          * @event actioncomplete
46898          * Fires when an action is completed.
46899          * @param {Form} this
46900          * @param {Action} action The action that completed
46901          */
46902         actioncomplete : true
46903     });
46904     if(el){
46905         this.initEl(el);
46906     }
46907     Roo.form.BasicForm.superclass.constructor.call(this);
46908     
46909     Roo.form.BasicForm.popover.apply();
46910 };
46911
46912 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46913     /**
46914      * @cfg {String} method
46915      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46916      */
46917     /**
46918      * @cfg {DataReader} reader
46919      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46920      * This is optional as there is built-in support for processing JSON.
46921      */
46922     /**
46923      * @cfg {DataReader} errorReader
46924      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46925      * This is completely optional as there is built-in support for processing JSON.
46926      */
46927     /**
46928      * @cfg {String} url
46929      * The URL to use for form actions if one isn't supplied in the action options.
46930      */
46931     /**
46932      * @cfg {Boolean} fileUpload
46933      * Set to true if this form is a file upload.
46934      */
46935      
46936     /**
46937      * @cfg {Object} baseParams
46938      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46939      */
46940      /**
46941      
46942     /**
46943      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46944      */
46945     timeout: 30,
46946
46947     // private
46948     activeAction : null,
46949
46950     /**
46951      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46952      * or setValues() data instead of when the form was first created.
46953      */
46954     trackResetOnLoad : false,
46955     
46956     
46957     /**
46958      * childForms - used for multi-tab forms
46959      * @type {Array}
46960      */
46961     childForms : false,
46962     
46963     /**
46964      * allItems - full list of fields.
46965      * @type {Array}
46966      */
46967     allItems : false,
46968     
46969     /**
46970      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46971      * element by passing it or its id or mask the form itself by passing in true.
46972      * @type Mixed
46973      */
46974     waitMsgTarget : false,
46975     
46976     /**
46977      * @type Boolean
46978      */
46979     disableMask : false,
46980     
46981     /**
46982      * @cfg {Boolean} errorMask (true|false) default false
46983      */
46984     errorMask : false,
46985     
46986     /**
46987      * @cfg {Number} maskOffset Default 100
46988      */
46989     maskOffset : 100,
46990
46991     // private
46992     initEl : function(el){
46993         this.el = Roo.get(el);
46994         this.id = this.el.id || Roo.id();
46995         this.el.on('submit', this.onSubmit, this);
46996         this.el.addClass('x-form');
46997     },
46998
46999     // private
47000     onSubmit : function(e){
47001         e.stopEvent();
47002     },
47003
47004     /**
47005      * Returns true if client-side validation on the form is successful.
47006      * @return Boolean
47007      */
47008     isValid : function(){
47009         var valid = true;
47010         var target = false;
47011         this.items.each(function(f){
47012             if(f.validate()){
47013                 return;
47014             }
47015             
47016             valid = false;
47017                 
47018             if(!target && f.el.isVisible(true)){
47019                 target = f;
47020             }
47021         });
47022         
47023         if(this.errorMask && !valid){
47024             Roo.form.BasicForm.popover.mask(this, target);
47025         }
47026         
47027         return valid;
47028     },
47029
47030     /**
47031      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47032      * @return Boolean
47033      */
47034     isDirty : function(){
47035         var dirty = false;
47036         this.items.each(function(f){
47037            if(f.isDirty()){
47038                dirty = true;
47039                return false;
47040            }
47041         });
47042         return dirty;
47043     },
47044     
47045     /**
47046      * Returns true if any fields in this form have changed since their original load. (New version)
47047      * @return Boolean
47048      */
47049     
47050     hasChanged : function()
47051     {
47052         var dirty = false;
47053         this.items.each(function(f){
47054            if(f.hasChanged()){
47055                dirty = true;
47056                return false;
47057            }
47058         });
47059         return dirty;
47060         
47061     },
47062     /**
47063      * Resets all hasChanged to 'false' -
47064      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47065      * So hasChanged storage is only to be used for this purpose
47066      * @return Boolean
47067      */
47068     resetHasChanged : function()
47069     {
47070         this.items.each(function(f){
47071            f.resetHasChanged();
47072         });
47073         
47074     },
47075     
47076     
47077     /**
47078      * Performs a predefined action (submit or load) or custom actions you define on this form.
47079      * @param {String} actionName The name of the action type
47080      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47081      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47082      * accept other config options):
47083      * <pre>
47084 Property          Type             Description
47085 ----------------  ---------------  ----------------------------------------------------------------------------------
47086 url               String           The url for the action (defaults to the form's url)
47087 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47088 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47089 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47090                                    validate the form on the client (defaults to false)
47091      * </pre>
47092      * @return {BasicForm} this
47093      */
47094     doAction : function(action, options){
47095         if(typeof action == 'string'){
47096             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47097         }
47098         if(this.fireEvent('beforeaction', this, action) !== false){
47099             this.beforeAction(action);
47100             action.run.defer(100, action);
47101         }
47102         return this;
47103     },
47104
47105     /**
47106      * Shortcut to do a submit action.
47107      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47108      * @return {BasicForm} this
47109      */
47110     submit : function(options){
47111         this.doAction('submit', options);
47112         return this;
47113     },
47114
47115     /**
47116      * Shortcut to do a load action.
47117      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47118      * @return {BasicForm} this
47119      */
47120     load : function(options){
47121         this.doAction('load', options);
47122         return this;
47123     },
47124
47125     /**
47126      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47127      * @param {Record} record The record to edit
47128      * @return {BasicForm} this
47129      */
47130     updateRecord : function(record){
47131         record.beginEdit();
47132         var fs = record.fields;
47133         fs.each(function(f){
47134             var field = this.findField(f.name);
47135             if(field){
47136                 record.set(f.name, field.getValue());
47137             }
47138         }, this);
47139         record.endEdit();
47140         return this;
47141     },
47142
47143     /**
47144      * Loads an Roo.data.Record into this form.
47145      * @param {Record} record The record to load
47146      * @return {BasicForm} this
47147      */
47148     loadRecord : function(record){
47149         this.setValues(record.data);
47150         return this;
47151     },
47152
47153     // private
47154     beforeAction : function(action){
47155         var o = action.options;
47156         
47157         if(!this.disableMask) {
47158             if(this.waitMsgTarget === true){
47159                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47160             }else if(this.waitMsgTarget){
47161                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47162                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47163             }else {
47164                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47165             }
47166         }
47167         
47168          
47169     },
47170
47171     // private
47172     afterAction : function(action, success){
47173         this.activeAction = null;
47174         var o = action.options;
47175         
47176         if(!this.disableMask) {
47177             if(this.waitMsgTarget === true){
47178                 this.el.unmask();
47179             }else if(this.waitMsgTarget){
47180                 this.waitMsgTarget.unmask();
47181             }else{
47182                 Roo.MessageBox.updateProgress(1);
47183                 Roo.MessageBox.hide();
47184             }
47185         }
47186         
47187         if(success){
47188             if(o.reset){
47189                 this.reset();
47190             }
47191             Roo.callback(o.success, o.scope, [this, action]);
47192             this.fireEvent('actioncomplete', this, action);
47193             
47194         }else{
47195             
47196             // failure condition..
47197             // we have a scenario where updates need confirming.
47198             // eg. if a locking scenario exists..
47199             // we look for { errors : { needs_confirm : true }} in the response.
47200             if (
47201                 (typeof(action.result) != 'undefined')  &&
47202                 (typeof(action.result.errors) != 'undefined')  &&
47203                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47204            ){
47205                 var _t = this;
47206                 Roo.MessageBox.confirm(
47207                     "Change requires confirmation",
47208                     action.result.errorMsg,
47209                     function(r) {
47210                         if (r != 'yes') {
47211                             return;
47212                         }
47213                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47214                     }
47215                     
47216                 );
47217                 
47218                 
47219                 
47220                 return;
47221             }
47222             
47223             Roo.callback(o.failure, o.scope, [this, action]);
47224             // show an error message if no failed handler is set..
47225             if (!this.hasListener('actionfailed')) {
47226                 Roo.MessageBox.alert("Error",
47227                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47228                         action.result.errorMsg :
47229                         "Saving Failed, please check your entries or try again"
47230                 );
47231             }
47232             
47233             this.fireEvent('actionfailed', this, action);
47234         }
47235         
47236     },
47237
47238     /**
47239      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47240      * @param {String} id The value to search for
47241      * @return Field
47242      */
47243     findField : function(id){
47244         var field = this.items.get(id);
47245         if(!field){
47246             this.items.each(function(f){
47247                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47248                     field = f;
47249                     return false;
47250                 }
47251             });
47252         }
47253         return field || null;
47254     },
47255
47256     /**
47257      * Add a secondary form to this one, 
47258      * Used to provide tabbed forms. One form is primary, with hidden values 
47259      * which mirror the elements from the other forms.
47260      * 
47261      * @param {Roo.form.Form} form to add.
47262      * 
47263      */
47264     addForm : function(form)
47265     {
47266        
47267         if (this.childForms.indexOf(form) > -1) {
47268             // already added..
47269             return;
47270         }
47271         this.childForms.push(form);
47272         var n = '';
47273         Roo.each(form.allItems, function (fe) {
47274             
47275             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47276             if (this.findField(n)) { // already added..
47277                 return;
47278             }
47279             var add = new Roo.form.Hidden({
47280                 name : n
47281             });
47282             add.render(this.el);
47283             
47284             this.add( add );
47285         }, this);
47286         
47287     },
47288     /**
47289      * Mark fields in this form invalid in bulk.
47290      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47291      * @return {BasicForm} this
47292      */
47293     markInvalid : function(errors){
47294         if(errors instanceof Array){
47295             for(var i = 0, len = errors.length; i < len; i++){
47296                 var fieldError = errors[i];
47297                 var f = this.findField(fieldError.id);
47298                 if(f){
47299                     f.markInvalid(fieldError.msg);
47300                 }
47301             }
47302         }else{
47303             var field, id;
47304             for(id in errors){
47305                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47306                     field.markInvalid(errors[id]);
47307                 }
47308             }
47309         }
47310         Roo.each(this.childForms || [], function (f) {
47311             f.markInvalid(errors);
47312         });
47313         
47314         return this;
47315     },
47316
47317     /**
47318      * Set values for fields in this form in bulk.
47319      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47320      * @return {BasicForm} this
47321      */
47322     setValues : function(values){
47323         if(values instanceof Array){ // array of objects
47324             for(var i = 0, len = values.length; i < len; i++){
47325                 var v = values[i];
47326                 var f = this.findField(v.id);
47327                 if(f){
47328                     f.setValue(v.value);
47329                     if(this.trackResetOnLoad){
47330                         f.originalValue = f.getValue();
47331                     }
47332                 }
47333             }
47334         }else{ // object hash
47335             var field, id;
47336             for(id in values){
47337                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47338                     
47339                     if (field.setFromData && 
47340                         field.valueField && 
47341                         field.displayField &&
47342                         // combos' with local stores can 
47343                         // be queried via setValue()
47344                         // to set their value..
47345                         (field.store && !field.store.isLocal)
47346                         ) {
47347                         // it's a combo
47348                         var sd = { };
47349                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47350                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47351                         field.setFromData(sd);
47352                         
47353                     } else {
47354                         field.setValue(values[id]);
47355                     }
47356                     
47357                     
47358                     if(this.trackResetOnLoad){
47359                         field.originalValue = field.getValue();
47360                     }
47361                 }
47362             }
47363         }
47364         this.resetHasChanged();
47365         
47366         
47367         Roo.each(this.childForms || [], function (f) {
47368             f.setValues(values);
47369             f.resetHasChanged();
47370         });
47371                 
47372         return this;
47373     },
47374  
47375     /**
47376      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47377      * they are returned as an array.
47378      * @param {Boolean} asString
47379      * @return {Object}
47380      */
47381     getValues : function(asString){
47382         if (this.childForms) {
47383             // copy values from the child forms
47384             Roo.each(this.childForms, function (f) {
47385                 this.setValues(f.getValues());
47386             }, this);
47387         }
47388         
47389         // use formdata
47390         if (typeof(FormData) != 'undefined' && asString !== true) {
47391             var fd = (new FormData(this.el.dom)).entries();
47392             var ret = {};
47393             var ent = fd.next();
47394             while (!ent.done) {
47395                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47396                 ent = fd.next();
47397             };
47398             return ret;
47399         }
47400         
47401         
47402         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47403         if(asString === true){
47404             return fs;
47405         }
47406         return Roo.urlDecode(fs);
47407     },
47408     
47409     /**
47410      * Returns the fields in this form as an object with key/value pairs. 
47411      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47412      * @return {Object}
47413      */
47414     getFieldValues : function(with_hidden)
47415     {
47416         if (this.childForms) {
47417             // copy values from the child forms
47418             // should this call getFieldValues - probably not as we do not currently copy
47419             // hidden fields when we generate..
47420             Roo.each(this.childForms, function (f) {
47421                 this.setValues(f.getValues());
47422             }, this);
47423         }
47424         
47425         var ret = {};
47426         this.items.each(function(f){
47427             if (!f.getName()) {
47428                 return;
47429             }
47430             var v = f.getValue();
47431             if (f.inputType =='radio') {
47432                 if (typeof(ret[f.getName()]) == 'undefined') {
47433                     ret[f.getName()] = ''; // empty..
47434                 }
47435                 
47436                 if (!f.el.dom.checked) {
47437                     return;
47438                     
47439                 }
47440                 v = f.el.dom.value;
47441                 
47442             }
47443             
47444             // not sure if this supported any more..
47445             if ((typeof(v) == 'object') && f.getRawValue) {
47446                 v = f.getRawValue() ; // dates..
47447             }
47448             // combo boxes where name != hiddenName...
47449             if (f.name != f.getName()) {
47450                 ret[f.name] = f.getRawValue();
47451             }
47452             ret[f.getName()] = v;
47453         });
47454         
47455         return ret;
47456     },
47457
47458     /**
47459      * Clears all invalid messages in this form.
47460      * @return {BasicForm} this
47461      */
47462     clearInvalid : function(){
47463         this.items.each(function(f){
47464            f.clearInvalid();
47465         });
47466         
47467         Roo.each(this.childForms || [], function (f) {
47468             f.clearInvalid();
47469         });
47470         
47471         
47472         return this;
47473     },
47474
47475     /**
47476      * Resets this form.
47477      * @return {BasicForm} this
47478      */
47479     reset : function(){
47480         this.items.each(function(f){
47481             f.reset();
47482         });
47483         
47484         Roo.each(this.childForms || [], function (f) {
47485             f.reset();
47486         });
47487         this.resetHasChanged();
47488         
47489         return this;
47490     },
47491
47492     /**
47493      * Add Roo.form components to this form.
47494      * @param {Field} field1
47495      * @param {Field} field2 (optional)
47496      * @param {Field} etc (optional)
47497      * @return {BasicForm} this
47498      */
47499     add : function(){
47500         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47501         return this;
47502     },
47503
47504
47505     /**
47506      * Removes a field from the items collection (does NOT remove its markup).
47507      * @param {Field} field
47508      * @return {BasicForm} this
47509      */
47510     remove : function(field){
47511         this.items.remove(field);
47512         return this;
47513     },
47514
47515     /**
47516      * Looks at the fields in this form, checks them for an id attribute,
47517      * and calls applyTo on the existing dom element with that id.
47518      * @return {BasicForm} this
47519      */
47520     render : function(){
47521         this.items.each(function(f){
47522             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47523                 f.applyTo(f.id);
47524             }
47525         });
47526         return this;
47527     },
47528
47529     /**
47530      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47531      * @param {Object} values
47532      * @return {BasicForm} this
47533      */
47534     applyToFields : function(o){
47535         this.items.each(function(f){
47536            Roo.apply(f, o);
47537         });
47538         return this;
47539     },
47540
47541     /**
47542      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47543      * @param {Object} values
47544      * @return {BasicForm} this
47545      */
47546     applyIfToFields : function(o){
47547         this.items.each(function(f){
47548            Roo.applyIf(f, o);
47549         });
47550         return this;
47551     }
47552 });
47553
47554 // back compat
47555 Roo.BasicForm = Roo.form.BasicForm;
47556
47557 Roo.apply(Roo.form.BasicForm, {
47558     
47559     popover : {
47560         
47561         padding : 5,
47562         
47563         isApplied : false,
47564         
47565         isMasked : false,
47566         
47567         form : false,
47568         
47569         target : false,
47570         
47571         intervalID : false,
47572         
47573         maskEl : false,
47574         
47575         apply : function()
47576         {
47577             if(this.isApplied){
47578                 return;
47579             }
47580             
47581             this.maskEl = {
47582                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47583                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47584                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47585                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47586             };
47587             
47588             this.maskEl.top.enableDisplayMode("block");
47589             this.maskEl.left.enableDisplayMode("block");
47590             this.maskEl.bottom.enableDisplayMode("block");
47591             this.maskEl.right.enableDisplayMode("block");
47592             
47593             Roo.get(document.body).on('click', function(){
47594                 this.unmask();
47595             }, this);
47596             
47597             Roo.get(document.body).on('touchstart', function(){
47598                 this.unmask();
47599             }, this);
47600             
47601             this.isApplied = true
47602         },
47603         
47604         mask : function(form, target)
47605         {
47606             this.form = form;
47607             
47608             this.target = target;
47609             
47610             if(!this.form.errorMask || !target.el){
47611                 return;
47612             }
47613             
47614             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47615             
47616             var ot = this.target.el.calcOffsetsTo(scrollable);
47617             
47618             var scrollTo = ot[1] - this.form.maskOffset;
47619             
47620             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47621             
47622             scrollable.scrollTo('top', scrollTo);
47623             
47624             var el = this.target.wrap || this.target.el;
47625             
47626             var box = el.getBox();
47627             
47628             this.maskEl.top.setStyle('position', 'absolute');
47629             this.maskEl.top.setStyle('z-index', 10000);
47630             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47631             this.maskEl.top.setLeft(0);
47632             this.maskEl.top.setTop(0);
47633             this.maskEl.top.show();
47634             
47635             this.maskEl.left.setStyle('position', 'absolute');
47636             this.maskEl.left.setStyle('z-index', 10000);
47637             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47638             this.maskEl.left.setLeft(0);
47639             this.maskEl.left.setTop(box.y - this.padding);
47640             this.maskEl.left.show();
47641
47642             this.maskEl.bottom.setStyle('position', 'absolute');
47643             this.maskEl.bottom.setStyle('z-index', 10000);
47644             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47645             this.maskEl.bottom.setLeft(0);
47646             this.maskEl.bottom.setTop(box.bottom + this.padding);
47647             this.maskEl.bottom.show();
47648
47649             this.maskEl.right.setStyle('position', 'absolute');
47650             this.maskEl.right.setStyle('z-index', 10000);
47651             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
47652             this.maskEl.right.setLeft(box.right + this.padding);
47653             this.maskEl.right.setTop(box.y - this.padding);
47654             this.maskEl.right.show();
47655
47656             this.intervalID = window.setInterval(function() {
47657                 Roo.form.BasicForm.popover.unmask();
47658             }, 10000);
47659
47660             window.onwheel = function(){ return false;};
47661             
47662             (function(){ this.isMasked = true; }).defer(500, this);
47663             
47664         },
47665         
47666         unmask : function()
47667         {
47668             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
47669                 return;
47670             }
47671             
47672             this.maskEl.top.setStyle('position', 'absolute');
47673             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
47674             this.maskEl.top.hide();
47675
47676             this.maskEl.left.setStyle('position', 'absolute');
47677             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
47678             this.maskEl.left.hide();
47679
47680             this.maskEl.bottom.setStyle('position', 'absolute');
47681             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
47682             this.maskEl.bottom.hide();
47683
47684             this.maskEl.right.setStyle('position', 'absolute');
47685             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
47686             this.maskEl.right.hide();
47687             
47688             window.onwheel = function(){ return true;};
47689             
47690             if(this.intervalID){
47691                 window.clearInterval(this.intervalID);
47692                 this.intervalID = false;
47693             }
47694             
47695             this.isMasked = false;
47696             
47697         }
47698         
47699     }
47700     
47701 });/*
47702  * Based on:
47703  * Ext JS Library 1.1.1
47704  * Copyright(c) 2006-2007, Ext JS, LLC.
47705  *
47706  * Originally Released Under LGPL - original licence link has changed is not relivant.
47707  *
47708  * Fork - LGPL
47709  * <script type="text/javascript">
47710  */
47711
47712 /**
47713  * @class Roo.form.Form
47714  * @extends Roo.form.BasicForm
47715  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47716  * @constructor
47717  * @param {Object} config Configuration options
47718  */
47719 Roo.form.Form = function(config){
47720     var xitems =  [];
47721     if (config.items) {
47722         xitems = config.items;
47723         delete config.items;
47724     }
47725    
47726     
47727     Roo.form.Form.superclass.constructor.call(this, null, config);
47728     this.url = this.url || this.action;
47729     if(!this.root){
47730         this.root = new Roo.form.Layout(Roo.applyIf({
47731             id: Roo.id()
47732         }, config));
47733     }
47734     this.active = this.root;
47735     /**
47736      * Array of all the buttons that have been added to this form via {@link addButton}
47737      * @type Array
47738      */
47739     this.buttons = [];
47740     this.allItems = [];
47741     this.addEvents({
47742         /**
47743          * @event clientvalidation
47744          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47745          * @param {Form} this
47746          * @param {Boolean} valid true if the form has passed client-side validation
47747          */
47748         clientvalidation: true,
47749         /**
47750          * @event rendered
47751          * Fires when the form is rendered
47752          * @param {Roo.form.Form} form
47753          */
47754         rendered : true
47755     });
47756     
47757     if (this.progressUrl) {
47758             // push a hidden field onto the list of fields..
47759             this.addxtype( {
47760                     xns: Roo.form, 
47761                     xtype : 'Hidden', 
47762                     name : 'UPLOAD_IDENTIFIER' 
47763             });
47764         }
47765         
47766     
47767     Roo.each(xitems, this.addxtype, this);
47768     
47769 };
47770
47771 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47772     /**
47773      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47774      */
47775     /**
47776      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47777      */
47778     /**
47779      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47780      */
47781     buttonAlign:'center',
47782
47783     /**
47784      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47785      */
47786     minButtonWidth:75,
47787
47788     /**
47789      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47790      * This property cascades to child containers if not set.
47791      */
47792     labelAlign:'left',
47793
47794     /**
47795      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47796      * fires a looping event with that state. This is required to bind buttons to the valid
47797      * state using the config value formBind:true on the button.
47798      */
47799     monitorValid : false,
47800
47801     /**
47802      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47803      */
47804     monitorPoll : 200,
47805     
47806     /**
47807      * @cfg {String} progressUrl - Url to return progress data 
47808      */
47809     
47810     progressUrl : false,
47811     /**
47812      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
47813      * sending a formdata with extra parameters - eg uploaded elements.
47814      */
47815     
47816     formData : false,
47817     
47818     /**
47819      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47820      * fields are added and the column is closed. If no fields are passed the column remains open
47821      * until end() is called.
47822      * @param {Object} config The config to pass to the column
47823      * @param {Field} field1 (optional)
47824      * @param {Field} field2 (optional)
47825      * @param {Field} etc (optional)
47826      * @return Column The column container object
47827      */
47828     column : function(c){
47829         var col = new Roo.form.Column(c);
47830         this.start(col);
47831         if(arguments.length > 1){ // duplicate code required because of Opera
47832             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47833             this.end();
47834         }
47835         return col;
47836     },
47837
47838     /**
47839      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47840      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47841      * until end() is called.
47842      * @param {Object} config The config to pass to the fieldset
47843      * @param {Field} field1 (optional)
47844      * @param {Field} field2 (optional)
47845      * @param {Field} etc (optional)
47846      * @return FieldSet The fieldset container object
47847      */
47848     fieldset : function(c){
47849         var fs = new Roo.form.FieldSet(c);
47850         this.start(fs);
47851         if(arguments.length > 1){ // duplicate code required because of Opera
47852             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47853             this.end();
47854         }
47855         return fs;
47856     },
47857
47858     /**
47859      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47860      * fields are added and the container is closed. If no fields are passed the container remains open
47861      * until end() is called.
47862      * @param {Object} config The config to pass to the Layout
47863      * @param {Field} field1 (optional)
47864      * @param {Field} field2 (optional)
47865      * @param {Field} etc (optional)
47866      * @return Layout The container object
47867      */
47868     container : function(c){
47869         var l = new Roo.form.Layout(c);
47870         this.start(l);
47871         if(arguments.length > 1){ // duplicate code required because of Opera
47872             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47873             this.end();
47874         }
47875         return l;
47876     },
47877
47878     /**
47879      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47880      * @param {Object} container A Roo.form.Layout or subclass of Layout
47881      * @return {Form} this
47882      */
47883     start : function(c){
47884         // cascade label info
47885         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47886         this.active.stack.push(c);
47887         c.ownerCt = this.active;
47888         this.active = c;
47889         return this;
47890     },
47891
47892     /**
47893      * Closes the current open container
47894      * @return {Form} this
47895      */
47896     end : function(){
47897         if(this.active == this.root){
47898             return this;
47899         }
47900         this.active = this.active.ownerCt;
47901         return this;
47902     },
47903
47904     /**
47905      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47906      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47907      * as the label of the field.
47908      * @param {Field} field1
47909      * @param {Field} field2 (optional)
47910      * @param {Field} etc. (optional)
47911      * @return {Form} this
47912      */
47913     add : function(){
47914         this.active.stack.push.apply(this.active.stack, arguments);
47915         this.allItems.push.apply(this.allItems,arguments);
47916         var r = [];
47917         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47918             if(a[i].isFormField){
47919                 r.push(a[i]);
47920             }
47921         }
47922         if(r.length > 0){
47923             Roo.form.Form.superclass.add.apply(this, r);
47924         }
47925         return this;
47926     },
47927     
47928
47929     
47930     
47931     
47932      /**
47933      * Find any element that has been added to a form, using it's ID or name
47934      * This can include framesets, columns etc. along with regular fields..
47935      * @param {String} id - id or name to find.
47936      
47937      * @return {Element} e - or false if nothing found.
47938      */
47939     findbyId : function(id)
47940     {
47941         var ret = false;
47942         if (!id) {
47943             return ret;
47944         }
47945         Roo.each(this.allItems, function(f){
47946             if (f.id == id || f.name == id ){
47947                 ret = f;
47948                 return false;
47949             }
47950         });
47951         return ret;
47952     },
47953
47954     
47955     
47956     /**
47957      * Render this form into the passed container. This should only be called once!
47958      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47959      * @return {Form} this
47960      */
47961     render : function(ct)
47962     {
47963         
47964         
47965         
47966         ct = Roo.get(ct);
47967         var o = this.autoCreate || {
47968             tag: 'form',
47969             method : this.method || 'POST',
47970             id : this.id || Roo.id()
47971         };
47972         this.initEl(ct.createChild(o));
47973
47974         this.root.render(this.el);
47975         
47976        
47977              
47978         this.items.each(function(f){
47979             f.render('x-form-el-'+f.id);
47980         });
47981
47982         if(this.buttons.length > 0){
47983             // tables are required to maintain order and for correct IE layout
47984             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47985                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47986                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47987             }}, null, true);
47988             var tr = tb.getElementsByTagName('tr')[0];
47989             for(var i = 0, len = this.buttons.length; i < len; i++) {
47990                 var b = this.buttons[i];
47991                 var td = document.createElement('td');
47992                 td.className = 'x-form-btn-td';
47993                 b.render(tr.appendChild(td));
47994             }
47995         }
47996         if(this.monitorValid){ // initialize after render
47997             this.startMonitoring();
47998         }
47999         this.fireEvent('rendered', this);
48000         return this;
48001     },
48002
48003     /**
48004      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48005      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48006      * object or a valid Roo.DomHelper element config
48007      * @param {Function} handler The function called when the button is clicked
48008      * @param {Object} scope (optional) The scope of the handler function
48009      * @return {Roo.Button}
48010      */
48011     addButton : function(config, handler, scope){
48012         var bc = {
48013             handler: handler,
48014             scope: scope,
48015             minWidth: this.minButtonWidth,
48016             hideParent:true
48017         };
48018         if(typeof config == "string"){
48019             bc.text = config;
48020         }else{
48021             Roo.apply(bc, config);
48022         }
48023         var btn = new Roo.Button(null, bc);
48024         this.buttons.push(btn);
48025         return btn;
48026     },
48027
48028      /**
48029      * Adds a series of form elements (using the xtype property as the factory method.
48030      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48031      * @param {Object} config 
48032      */
48033     
48034     addxtype : function()
48035     {
48036         var ar = Array.prototype.slice.call(arguments, 0);
48037         var ret = false;
48038         for(var i = 0; i < ar.length; i++) {
48039             if (!ar[i]) {
48040                 continue; // skip -- if this happends something invalid got sent, we 
48041                 // should ignore it, as basically that interface element will not show up
48042                 // and that should be pretty obvious!!
48043             }
48044             
48045             if (Roo.form[ar[i].xtype]) {
48046                 ar[i].form = this;
48047                 var fe = Roo.factory(ar[i], Roo.form);
48048                 if (!ret) {
48049                     ret = fe;
48050                 }
48051                 fe.form = this;
48052                 if (fe.store) {
48053                     fe.store.form = this;
48054                 }
48055                 if (fe.isLayout) {  
48056                          
48057                     this.start(fe);
48058                     this.allItems.push(fe);
48059                     if (fe.items && fe.addxtype) {
48060                         fe.addxtype.apply(fe, fe.items);
48061                         delete fe.items;
48062                     }
48063                      this.end();
48064                     continue;
48065                 }
48066                 
48067                 
48068                  
48069                 this.add(fe);
48070               //  console.log('adding ' + ar[i].xtype);
48071             }
48072             if (ar[i].xtype == 'Button') {  
48073                 //console.log('adding button');
48074                 //console.log(ar[i]);
48075                 this.addButton(ar[i]);
48076                 this.allItems.push(fe);
48077                 continue;
48078             }
48079             
48080             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48081                 alert('end is not supported on xtype any more, use items');
48082             //    this.end();
48083             //    //console.log('adding end');
48084             }
48085             
48086         }
48087         return ret;
48088     },
48089     
48090     /**
48091      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48092      * option "monitorValid"
48093      */
48094     startMonitoring : function(){
48095         if(!this.bound){
48096             this.bound = true;
48097             Roo.TaskMgr.start({
48098                 run : this.bindHandler,
48099                 interval : this.monitorPoll || 200,
48100                 scope: this
48101             });
48102         }
48103     },
48104
48105     /**
48106      * Stops monitoring of the valid state of this form
48107      */
48108     stopMonitoring : function(){
48109         this.bound = false;
48110     },
48111
48112     // private
48113     bindHandler : function(){
48114         if(!this.bound){
48115             return false; // stops binding
48116         }
48117         var valid = true;
48118         this.items.each(function(f){
48119             if(!f.isValid(true)){
48120                 valid = false;
48121                 return false;
48122             }
48123         });
48124         for(var i = 0, len = this.buttons.length; i < len; i++){
48125             var btn = this.buttons[i];
48126             if(btn.formBind === true && btn.disabled === valid){
48127                 btn.setDisabled(!valid);
48128             }
48129         }
48130         this.fireEvent('clientvalidation', this, valid);
48131     }
48132     
48133     
48134     
48135     
48136     
48137     
48138     
48139     
48140 });
48141
48142
48143 // back compat
48144 Roo.Form = Roo.form.Form;
48145 /*
48146  * Based on:
48147  * Ext JS Library 1.1.1
48148  * Copyright(c) 2006-2007, Ext JS, LLC.
48149  *
48150  * Originally Released Under LGPL - original licence link has changed is not relivant.
48151  *
48152  * Fork - LGPL
48153  * <script type="text/javascript">
48154  */
48155
48156 // as we use this in bootstrap.
48157 Roo.namespace('Roo.form');
48158  /**
48159  * @class Roo.form.Action
48160  * Internal Class used to handle form actions
48161  * @constructor
48162  * @param {Roo.form.BasicForm} el The form element or its id
48163  * @param {Object} config Configuration options
48164  */
48165
48166  
48167  
48168 // define the action interface
48169 Roo.form.Action = function(form, options){
48170     this.form = form;
48171     this.options = options || {};
48172 };
48173 /**
48174  * Client Validation Failed
48175  * @const 
48176  */
48177 Roo.form.Action.CLIENT_INVALID = 'client';
48178 /**
48179  * Server Validation Failed
48180  * @const 
48181  */
48182 Roo.form.Action.SERVER_INVALID = 'server';
48183  /**
48184  * Connect to Server Failed
48185  * @const 
48186  */
48187 Roo.form.Action.CONNECT_FAILURE = 'connect';
48188 /**
48189  * Reading Data from Server Failed
48190  * @const 
48191  */
48192 Roo.form.Action.LOAD_FAILURE = 'load';
48193
48194 Roo.form.Action.prototype = {
48195     type : 'default',
48196     failureType : undefined,
48197     response : undefined,
48198     result : undefined,
48199
48200     // interface method
48201     run : function(options){
48202
48203     },
48204
48205     // interface method
48206     success : function(response){
48207
48208     },
48209
48210     // interface method
48211     handleResponse : function(response){
48212
48213     },
48214
48215     // default connection failure
48216     failure : function(response){
48217         
48218         this.response = response;
48219         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48220         this.form.afterAction(this, false);
48221     },
48222
48223     processResponse : function(response){
48224         this.response = response;
48225         if(!response.responseText){
48226             return true;
48227         }
48228         this.result = this.handleResponse(response);
48229         return this.result;
48230     },
48231
48232     // utility functions used internally
48233     getUrl : function(appendParams){
48234         var url = this.options.url || this.form.url || this.form.el.dom.action;
48235         if(appendParams){
48236             var p = this.getParams();
48237             if(p){
48238                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48239             }
48240         }
48241         return url;
48242     },
48243
48244     getMethod : function(){
48245         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48246     },
48247
48248     getParams : function(){
48249         var bp = this.form.baseParams;
48250         var p = this.options.params;
48251         if(p){
48252             if(typeof p == "object"){
48253                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48254             }else if(typeof p == 'string' && bp){
48255                 p += '&' + Roo.urlEncode(bp);
48256             }
48257         }else if(bp){
48258             p = Roo.urlEncode(bp);
48259         }
48260         return p;
48261     },
48262
48263     createCallback : function(){
48264         return {
48265             success: this.success,
48266             failure: this.failure,
48267             scope: this,
48268             timeout: (this.form.timeout*1000),
48269             upload: this.form.fileUpload ? this.success : undefined
48270         };
48271     }
48272 };
48273
48274 Roo.form.Action.Submit = function(form, options){
48275     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48276 };
48277
48278 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48279     type : 'submit',
48280
48281     haveProgress : false,
48282     uploadComplete : false,
48283     
48284     // uploadProgress indicator.
48285     uploadProgress : function()
48286     {
48287         if (!this.form.progressUrl) {
48288             return;
48289         }
48290         
48291         if (!this.haveProgress) {
48292             Roo.MessageBox.progress("Uploading", "Uploading");
48293         }
48294         if (this.uploadComplete) {
48295            Roo.MessageBox.hide();
48296            return;
48297         }
48298         
48299         this.haveProgress = true;
48300    
48301         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48302         
48303         var c = new Roo.data.Connection();
48304         c.request({
48305             url : this.form.progressUrl,
48306             params: {
48307                 id : uid
48308             },
48309             method: 'GET',
48310             success : function(req){
48311                //console.log(data);
48312                 var rdata = false;
48313                 var edata;
48314                 try  {
48315                    rdata = Roo.decode(req.responseText)
48316                 } catch (e) {
48317                     Roo.log("Invalid data from server..");
48318                     Roo.log(edata);
48319                     return;
48320                 }
48321                 if (!rdata || !rdata.success) {
48322                     Roo.log(rdata);
48323                     Roo.MessageBox.alert(Roo.encode(rdata));
48324                     return;
48325                 }
48326                 var data = rdata.data;
48327                 
48328                 if (this.uploadComplete) {
48329                    Roo.MessageBox.hide();
48330                    return;
48331                 }
48332                    
48333                 if (data){
48334                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48335                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48336                     );
48337                 }
48338                 this.uploadProgress.defer(2000,this);
48339             },
48340        
48341             failure: function(data) {
48342                 Roo.log('progress url failed ');
48343                 Roo.log(data);
48344             },
48345             scope : this
48346         });
48347            
48348     },
48349     
48350     
48351     run : function()
48352     {
48353         // run get Values on the form, so it syncs any secondary forms.
48354         this.form.getValues();
48355         
48356         var o = this.options;
48357         var method = this.getMethod();
48358         var isPost = method == 'POST';
48359         if(o.clientValidation === false || this.form.isValid()){
48360             
48361             if (this.form.progressUrl) {
48362                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48363                     (new Date() * 1) + '' + Math.random());
48364                     
48365             } 
48366             
48367             
48368             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48369                 form:this.form.el.dom,
48370                 url:this.getUrl(!isPost),
48371                 method: method,
48372                 params:isPost ? this.getParams() : null,
48373                 isUpload: this.form.fileUpload,
48374                 formData : this.form.formData
48375             }));
48376             
48377             this.uploadProgress();
48378
48379         }else if (o.clientValidation !== false){ // client validation failed
48380             this.failureType = Roo.form.Action.CLIENT_INVALID;
48381             this.form.afterAction(this, false);
48382         }
48383     },
48384
48385     success : function(response)
48386     {
48387         this.uploadComplete= true;
48388         if (this.haveProgress) {
48389             Roo.MessageBox.hide();
48390         }
48391         
48392         
48393         var result = this.processResponse(response);
48394         if(result === true || result.success){
48395             this.form.afterAction(this, true);
48396             return;
48397         }
48398         if(result.errors){
48399             this.form.markInvalid(result.errors);
48400             this.failureType = Roo.form.Action.SERVER_INVALID;
48401         }
48402         this.form.afterAction(this, false);
48403     },
48404     failure : function(response)
48405     {
48406         this.uploadComplete= true;
48407         if (this.haveProgress) {
48408             Roo.MessageBox.hide();
48409         }
48410         
48411         this.response = response;
48412         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48413         this.form.afterAction(this, false);
48414     },
48415     
48416     handleResponse : function(response){
48417         if(this.form.errorReader){
48418             var rs = this.form.errorReader.read(response);
48419             var errors = [];
48420             if(rs.records){
48421                 for(var i = 0, len = rs.records.length; i < len; i++) {
48422                     var r = rs.records[i];
48423                     errors[i] = r.data;
48424                 }
48425             }
48426             if(errors.length < 1){
48427                 errors = null;
48428             }
48429             return {
48430                 success : rs.success,
48431                 errors : errors
48432             };
48433         }
48434         var ret = false;
48435         try {
48436             ret = Roo.decode(response.responseText);
48437         } catch (e) {
48438             ret = {
48439                 success: false,
48440                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48441                 errors : []
48442             };
48443         }
48444         return ret;
48445         
48446     }
48447 });
48448
48449
48450 Roo.form.Action.Load = function(form, options){
48451     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48452     this.reader = this.form.reader;
48453 };
48454
48455 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48456     type : 'load',
48457
48458     run : function(){
48459         
48460         Roo.Ajax.request(Roo.apply(
48461                 this.createCallback(), {
48462                     method:this.getMethod(),
48463                     url:this.getUrl(false),
48464                     params:this.getParams()
48465         }));
48466     },
48467
48468     success : function(response){
48469         
48470         var result = this.processResponse(response);
48471         if(result === true || !result.success || !result.data){
48472             this.failureType = Roo.form.Action.LOAD_FAILURE;
48473             this.form.afterAction(this, false);
48474             return;
48475         }
48476         this.form.clearInvalid();
48477         this.form.setValues(result.data);
48478         this.form.afterAction(this, true);
48479     },
48480
48481     handleResponse : function(response){
48482         if(this.form.reader){
48483             var rs = this.form.reader.read(response);
48484             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48485             return {
48486                 success : rs.success,
48487                 data : data
48488             };
48489         }
48490         return Roo.decode(response.responseText);
48491     }
48492 });
48493
48494 Roo.form.Action.ACTION_TYPES = {
48495     'load' : Roo.form.Action.Load,
48496     'submit' : Roo.form.Action.Submit
48497 };/*
48498  * Based on:
48499  * Ext JS Library 1.1.1
48500  * Copyright(c) 2006-2007, Ext JS, LLC.
48501  *
48502  * Originally Released Under LGPL - original licence link has changed is not relivant.
48503  *
48504  * Fork - LGPL
48505  * <script type="text/javascript">
48506  */
48507  
48508 /**
48509  * @class Roo.form.Layout
48510  * @extends Roo.Component
48511  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48512  * @constructor
48513  * @param {Object} config Configuration options
48514  */
48515 Roo.form.Layout = function(config){
48516     var xitems = [];
48517     if (config.items) {
48518         xitems = config.items;
48519         delete config.items;
48520     }
48521     Roo.form.Layout.superclass.constructor.call(this, config);
48522     this.stack = [];
48523     Roo.each(xitems, this.addxtype, this);
48524      
48525 };
48526
48527 Roo.extend(Roo.form.Layout, Roo.Component, {
48528     /**
48529      * @cfg {String/Object} autoCreate
48530      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48531      */
48532     /**
48533      * @cfg {String/Object/Function} style
48534      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48535      * a function which returns such a specification.
48536      */
48537     /**
48538      * @cfg {String} labelAlign
48539      * Valid values are "left," "top" and "right" (defaults to "left")
48540      */
48541     /**
48542      * @cfg {Number} labelWidth
48543      * Fixed width in pixels of all field labels (defaults to undefined)
48544      */
48545     /**
48546      * @cfg {Boolean} clear
48547      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48548      */
48549     clear : true,
48550     /**
48551      * @cfg {String} labelSeparator
48552      * The separator to use after field labels (defaults to ':')
48553      */
48554     labelSeparator : ':',
48555     /**
48556      * @cfg {Boolean} hideLabels
48557      * True to suppress the display of field labels in this layout (defaults to false)
48558      */
48559     hideLabels : false,
48560
48561     // private
48562     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48563     
48564     isLayout : true,
48565     
48566     // private
48567     onRender : function(ct, position){
48568         if(this.el){ // from markup
48569             this.el = Roo.get(this.el);
48570         }else {  // generate
48571             var cfg = this.getAutoCreate();
48572             this.el = ct.createChild(cfg, position);
48573         }
48574         if(this.style){
48575             this.el.applyStyles(this.style);
48576         }
48577         if(this.labelAlign){
48578             this.el.addClass('x-form-label-'+this.labelAlign);
48579         }
48580         if(this.hideLabels){
48581             this.labelStyle = "display:none";
48582             this.elementStyle = "padding-left:0;";
48583         }else{
48584             if(typeof this.labelWidth == 'number'){
48585                 this.labelStyle = "width:"+this.labelWidth+"px;";
48586                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48587             }
48588             if(this.labelAlign == 'top'){
48589                 this.labelStyle = "width:auto;";
48590                 this.elementStyle = "padding-left:0;";
48591             }
48592         }
48593         var stack = this.stack;
48594         var slen = stack.length;
48595         if(slen > 0){
48596             if(!this.fieldTpl){
48597                 var t = new Roo.Template(
48598                     '<div class="x-form-item {5}">',
48599                         '<label for="{0}" style="{2}">{1}{4}</label>',
48600                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48601                         '</div>',
48602                     '</div><div class="x-form-clear-left"></div>'
48603                 );
48604                 t.disableFormats = true;
48605                 t.compile();
48606                 Roo.form.Layout.prototype.fieldTpl = t;
48607             }
48608             for(var i = 0; i < slen; i++) {
48609                 if(stack[i].isFormField){
48610                     this.renderField(stack[i]);
48611                 }else{
48612                     this.renderComponent(stack[i]);
48613                 }
48614             }
48615         }
48616         if(this.clear){
48617             this.el.createChild({cls:'x-form-clear'});
48618         }
48619     },
48620
48621     // private
48622     renderField : function(f){
48623         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48624                f.id, //0
48625                f.fieldLabel, //1
48626                f.labelStyle||this.labelStyle||'', //2
48627                this.elementStyle||'', //3
48628                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48629                f.itemCls||this.itemCls||''  //5
48630        ], true).getPrevSibling());
48631     },
48632
48633     // private
48634     renderComponent : function(c){
48635         c.render(c.isLayout ? this.el : this.el.createChild());    
48636     },
48637     /**
48638      * Adds a object form elements (using the xtype property as the factory method.)
48639      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48640      * @param {Object} config 
48641      */
48642     addxtype : function(o)
48643     {
48644         // create the lement.
48645         o.form = this.form;
48646         var fe = Roo.factory(o, Roo.form);
48647         this.form.allItems.push(fe);
48648         this.stack.push(fe);
48649         
48650         if (fe.isFormField) {
48651             this.form.items.add(fe);
48652         }
48653          
48654         return fe;
48655     }
48656 });
48657
48658 /**
48659  * @class Roo.form.Column
48660  * @extends Roo.form.Layout
48661  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48662  * @constructor
48663  * @param {Object} config Configuration options
48664  */
48665 Roo.form.Column = function(config){
48666     Roo.form.Column.superclass.constructor.call(this, config);
48667 };
48668
48669 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48670     /**
48671      * @cfg {Number/String} width
48672      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48673      */
48674     /**
48675      * @cfg {String/Object} autoCreate
48676      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48677      */
48678
48679     // private
48680     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48681
48682     // private
48683     onRender : function(ct, position){
48684         Roo.form.Column.superclass.onRender.call(this, ct, position);
48685         if(this.width){
48686             this.el.setWidth(this.width);
48687         }
48688     }
48689 });
48690
48691
48692 /**
48693  * @class Roo.form.Row
48694  * @extends Roo.form.Layout
48695  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48696  * @constructor
48697  * @param {Object} config Configuration options
48698  */
48699
48700  
48701 Roo.form.Row = function(config){
48702     Roo.form.Row.superclass.constructor.call(this, config);
48703 };
48704  
48705 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48706       /**
48707      * @cfg {Number/String} width
48708      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48709      */
48710     /**
48711      * @cfg {Number/String} height
48712      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48713      */
48714     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48715     
48716     padWidth : 20,
48717     // private
48718     onRender : function(ct, position){
48719         //console.log('row render');
48720         if(!this.rowTpl){
48721             var t = new Roo.Template(
48722                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48723                     '<label for="{0}" style="{2}">{1}{4}</label>',
48724                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48725                     '</div>',
48726                 '</div>'
48727             );
48728             t.disableFormats = true;
48729             t.compile();
48730             Roo.form.Layout.prototype.rowTpl = t;
48731         }
48732         this.fieldTpl = this.rowTpl;
48733         
48734         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48735         var labelWidth = 100;
48736         
48737         if ((this.labelAlign != 'top')) {
48738             if (typeof this.labelWidth == 'number') {
48739                 labelWidth = this.labelWidth
48740             }
48741             this.padWidth =  20 + labelWidth;
48742             
48743         }
48744         
48745         Roo.form.Column.superclass.onRender.call(this, ct, position);
48746         if(this.width){
48747             this.el.setWidth(this.width);
48748         }
48749         if(this.height){
48750             this.el.setHeight(this.height);
48751         }
48752     },
48753     
48754     // private
48755     renderField : function(f){
48756         f.fieldEl = this.fieldTpl.append(this.el, [
48757                f.id, f.fieldLabel,
48758                f.labelStyle||this.labelStyle||'',
48759                this.elementStyle||'',
48760                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48761                f.itemCls||this.itemCls||'',
48762                f.width ? f.width + this.padWidth : 160 + this.padWidth
48763        ],true);
48764     }
48765 });
48766  
48767
48768 /**
48769  * @class Roo.form.FieldSet
48770  * @extends Roo.form.Layout
48771  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48772  * @constructor
48773  * @param {Object} config Configuration options
48774  */
48775 Roo.form.FieldSet = function(config){
48776     Roo.form.FieldSet.superclass.constructor.call(this, config);
48777 };
48778
48779 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48780     /**
48781      * @cfg {String} legend
48782      * The text to display as the legend for the FieldSet (defaults to '')
48783      */
48784     /**
48785      * @cfg {String/Object} autoCreate
48786      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48787      */
48788
48789     // private
48790     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48791
48792     // private
48793     onRender : function(ct, position){
48794         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48795         if(this.legend){
48796             this.setLegend(this.legend);
48797         }
48798     },
48799
48800     // private
48801     setLegend : function(text){
48802         if(this.rendered){
48803             this.el.child('legend').update(text);
48804         }
48805     }
48806 });/*
48807  * Based on:
48808  * Ext JS Library 1.1.1
48809  * Copyright(c) 2006-2007, Ext JS, LLC.
48810  *
48811  * Originally Released Under LGPL - original licence link has changed is not relivant.
48812  *
48813  * Fork - LGPL
48814  * <script type="text/javascript">
48815  */
48816 /**
48817  * @class Roo.form.VTypes
48818  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48819  * @singleton
48820  */
48821 Roo.form.VTypes = function(){
48822     // closure these in so they are only created once.
48823     var alpha = /^[a-zA-Z_]+$/;
48824     var alphanum = /^[a-zA-Z0-9_]+$/;
48825     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48826     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48827
48828     // All these messages and functions are configurable
48829     return {
48830         /**
48831          * The function used to validate email addresses
48832          * @param {String} value The email address
48833          */
48834         'email' : function(v){
48835             return email.test(v);
48836         },
48837         /**
48838          * The error text to display when the email validation function returns false
48839          * @type String
48840          */
48841         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48842         /**
48843          * The keystroke filter mask to be applied on email input
48844          * @type RegExp
48845          */
48846         'emailMask' : /[a-z0-9_\.\-@]/i,
48847
48848         /**
48849          * The function used to validate URLs
48850          * @param {String} value The URL
48851          */
48852         'url' : function(v){
48853             return url.test(v);
48854         },
48855         /**
48856          * The error text to display when the url validation function returns false
48857          * @type String
48858          */
48859         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48860         
48861         /**
48862          * The function used to validate alpha values
48863          * @param {String} value The value
48864          */
48865         'alpha' : function(v){
48866             return alpha.test(v);
48867         },
48868         /**
48869          * The error text to display when the alpha validation function returns false
48870          * @type String
48871          */
48872         'alphaText' : 'This field should only contain letters and _',
48873         /**
48874          * The keystroke filter mask to be applied on alpha input
48875          * @type RegExp
48876          */
48877         'alphaMask' : /[a-z_]/i,
48878
48879         /**
48880          * The function used to validate alphanumeric values
48881          * @param {String} value The value
48882          */
48883         'alphanum' : function(v){
48884             return alphanum.test(v);
48885         },
48886         /**
48887          * The error text to display when the alphanumeric validation function returns false
48888          * @type String
48889          */
48890         'alphanumText' : 'This field should only contain letters, numbers and _',
48891         /**
48892          * The keystroke filter mask to be applied on alphanumeric input
48893          * @type RegExp
48894          */
48895         'alphanumMask' : /[a-z0-9_]/i
48896     };
48897 }();//<script type="text/javascript">
48898
48899 /**
48900  * @class Roo.form.FCKeditor
48901  * @extends Roo.form.TextArea
48902  * Wrapper around the FCKEditor http://www.fckeditor.net
48903  * @constructor
48904  * Creates a new FCKeditor
48905  * @param {Object} config Configuration options
48906  */
48907 Roo.form.FCKeditor = function(config){
48908     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48909     this.addEvents({
48910          /**
48911          * @event editorinit
48912          * Fired when the editor is initialized - you can add extra handlers here..
48913          * @param {FCKeditor} this
48914          * @param {Object} the FCK object.
48915          */
48916         editorinit : true
48917     });
48918     
48919     
48920 };
48921 Roo.form.FCKeditor.editors = { };
48922 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48923 {
48924     //defaultAutoCreate : {
48925     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48926     //},
48927     // private
48928     /**
48929      * @cfg {Object} fck options - see fck manual for details.
48930      */
48931     fckconfig : false,
48932     
48933     /**
48934      * @cfg {Object} fck toolbar set (Basic or Default)
48935      */
48936     toolbarSet : 'Basic',
48937     /**
48938      * @cfg {Object} fck BasePath
48939      */ 
48940     basePath : '/fckeditor/',
48941     
48942     
48943     frame : false,
48944     
48945     value : '',
48946     
48947    
48948     onRender : function(ct, position)
48949     {
48950         if(!this.el){
48951             this.defaultAutoCreate = {
48952                 tag: "textarea",
48953                 style:"width:300px;height:60px;",
48954                 autocomplete: "new-password"
48955             };
48956         }
48957         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48958         /*
48959         if(this.grow){
48960             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48961             if(this.preventScrollbars){
48962                 this.el.setStyle("overflow", "hidden");
48963             }
48964             this.el.setHeight(this.growMin);
48965         }
48966         */
48967         //console.log('onrender' + this.getId() );
48968         Roo.form.FCKeditor.editors[this.getId()] = this;
48969          
48970
48971         this.replaceTextarea() ;
48972         
48973     },
48974     
48975     getEditor : function() {
48976         return this.fckEditor;
48977     },
48978     /**
48979      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48980      * @param {Mixed} value The value to set
48981      */
48982     
48983     
48984     setValue : function(value)
48985     {
48986         //console.log('setValue: ' + value);
48987         
48988         if(typeof(value) == 'undefined') { // not sure why this is happending...
48989             return;
48990         }
48991         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48992         
48993         //if(!this.el || !this.getEditor()) {
48994         //    this.value = value;
48995             //this.setValue.defer(100,this,[value]);    
48996         //    return;
48997         //} 
48998         
48999         if(!this.getEditor()) {
49000             return;
49001         }
49002         
49003         this.getEditor().SetData(value);
49004         
49005         //
49006
49007     },
49008
49009     /**
49010      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49011      * @return {Mixed} value The field value
49012      */
49013     getValue : function()
49014     {
49015         
49016         if (this.frame && this.frame.dom.style.display == 'none') {
49017             return Roo.form.FCKeditor.superclass.getValue.call(this);
49018         }
49019         
49020         if(!this.el || !this.getEditor()) {
49021            
49022            // this.getValue.defer(100,this); 
49023             return this.value;
49024         }
49025        
49026         
49027         var value=this.getEditor().GetData();
49028         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49029         return Roo.form.FCKeditor.superclass.getValue.call(this);
49030         
49031
49032     },
49033
49034     /**
49035      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49036      * @return {Mixed} value The field value
49037      */
49038     getRawValue : function()
49039     {
49040         if (this.frame && this.frame.dom.style.display == 'none') {
49041             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49042         }
49043         
49044         if(!this.el || !this.getEditor()) {
49045             //this.getRawValue.defer(100,this); 
49046             return this.value;
49047             return;
49048         }
49049         
49050         
49051         
49052         var value=this.getEditor().GetData();
49053         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49054         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49055          
49056     },
49057     
49058     setSize : function(w,h) {
49059         
49060         
49061         
49062         //if (this.frame && this.frame.dom.style.display == 'none') {
49063         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49064         //    return;
49065         //}
49066         //if(!this.el || !this.getEditor()) {
49067         //    this.setSize.defer(100,this, [w,h]); 
49068         //    return;
49069         //}
49070         
49071         
49072         
49073         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49074         
49075         this.frame.dom.setAttribute('width', w);
49076         this.frame.dom.setAttribute('height', h);
49077         this.frame.setSize(w,h);
49078         
49079     },
49080     
49081     toggleSourceEdit : function(value) {
49082         
49083       
49084          
49085         this.el.dom.style.display = value ? '' : 'none';
49086         this.frame.dom.style.display = value ?  'none' : '';
49087         
49088     },
49089     
49090     
49091     focus: function(tag)
49092     {
49093         if (this.frame.dom.style.display == 'none') {
49094             return Roo.form.FCKeditor.superclass.focus.call(this);
49095         }
49096         if(!this.el || !this.getEditor()) {
49097             this.focus.defer(100,this, [tag]); 
49098             return;
49099         }
49100         
49101         
49102         
49103         
49104         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49105         this.getEditor().Focus();
49106         if (tgs.length) {
49107             if (!this.getEditor().Selection.GetSelection()) {
49108                 this.focus.defer(100,this, [tag]); 
49109                 return;
49110             }
49111             
49112             
49113             var r = this.getEditor().EditorDocument.createRange();
49114             r.setStart(tgs[0],0);
49115             r.setEnd(tgs[0],0);
49116             this.getEditor().Selection.GetSelection().removeAllRanges();
49117             this.getEditor().Selection.GetSelection().addRange(r);
49118             this.getEditor().Focus();
49119         }
49120         
49121     },
49122     
49123     
49124     
49125     replaceTextarea : function()
49126     {
49127         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49128             return ;
49129         }
49130         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49131         //{
49132             // We must check the elements firstly using the Id and then the name.
49133         var oTextarea = document.getElementById( this.getId() );
49134         
49135         var colElementsByName = document.getElementsByName( this.getId() ) ;
49136          
49137         oTextarea.style.display = 'none' ;
49138
49139         if ( oTextarea.tabIndex ) {            
49140             this.TabIndex = oTextarea.tabIndex ;
49141         }
49142         
49143         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49144         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49145         this.frame = Roo.get(this.getId() + '___Frame')
49146     },
49147     
49148     _getConfigHtml : function()
49149     {
49150         var sConfig = '' ;
49151
49152         for ( var o in this.fckconfig ) {
49153             sConfig += sConfig.length > 0  ? '&amp;' : '';
49154             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49155         }
49156
49157         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49158     },
49159     
49160     
49161     _getIFrameHtml : function()
49162     {
49163         var sFile = 'fckeditor.html' ;
49164         /* no idea what this is about..
49165         try
49166         {
49167             if ( (/fcksource=true/i).test( window.top.location.search ) )
49168                 sFile = 'fckeditor.original.html' ;
49169         }
49170         catch (e) { 
49171         */
49172
49173         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49174         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49175         
49176         
49177         var html = '<iframe id="' + this.getId() +
49178             '___Frame" src="' + sLink +
49179             '" width="' + this.width +
49180             '" height="' + this.height + '"' +
49181             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49182             ' frameborder="0" scrolling="no"></iframe>' ;
49183
49184         return html ;
49185     },
49186     
49187     _insertHtmlBefore : function( html, element )
49188     {
49189         if ( element.insertAdjacentHTML )       {
49190             // IE
49191             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49192         } else { // Gecko
49193             var oRange = document.createRange() ;
49194             oRange.setStartBefore( element ) ;
49195             var oFragment = oRange.createContextualFragment( html );
49196             element.parentNode.insertBefore( oFragment, element ) ;
49197         }
49198     }
49199     
49200     
49201   
49202     
49203     
49204     
49205     
49206
49207 });
49208
49209 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49210
49211 function FCKeditor_OnComplete(editorInstance){
49212     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49213     f.fckEditor = editorInstance;
49214     //console.log("loaded");
49215     f.fireEvent('editorinit', f, editorInstance);
49216
49217   
49218
49219  
49220
49221
49222
49223
49224
49225
49226
49227
49228
49229
49230
49231
49232
49233
49234
49235 //<script type="text/javascript">
49236 /**
49237  * @class Roo.form.GridField
49238  * @extends Roo.form.Field
49239  * Embed a grid (or editable grid into a form)
49240  * STATUS ALPHA
49241  * 
49242  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49243  * it needs 
49244  * xgrid.store = Roo.data.Store
49245  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49246  * xgrid.store.reader = Roo.data.JsonReader 
49247  * 
49248  * 
49249  * @constructor
49250  * Creates a new GridField
49251  * @param {Object} config Configuration options
49252  */
49253 Roo.form.GridField = function(config){
49254     Roo.form.GridField.superclass.constructor.call(this, config);
49255      
49256 };
49257
49258 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49259     /**
49260      * @cfg {Number} width  - used to restrict width of grid..
49261      */
49262     width : 100,
49263     /**
49264      * @cfg {Number} height - used to restrict height of grid..
49265      */
49266     height : 50,
49267      /**
49268      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49269          * 
49270          *}
49271      */
49272     xgrid : false, 
49273     /**
49274      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49275      * {tag: "input", type: "checkbox", autocomplete: "off"})
49276      */
49277    // defaultAutoCreate : { tag: 'div' },
49278     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49279     /**
49280      * @cfg {String} addTitle Text to include for adding a title.
49281      */
49282     addTitle : false,
49283     //
49284     onResize : function(){
49285         Roo.form.Field.superclass.onResize.apply(this, arguments);
49286     },
49287
49288     initEvents : function(){
49289         // Roo.form.Checkbox.superclass.initEvents.call(this);
49290         // has no events...
49291        
49292     },
49293
49294
49295     getResizeEl : function(){
49296         return this.wrap;
49297     },
49298
49299     getPositionEl : function(){
49300         return this.wrap;
49301     },
49302
49303     // private
49304     onRender : function(ct, position){
49305         
49306         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49307         var style = this.style;
49308         delete this.style;
49309         
49310         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49311         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49312         this.viewEl = this.wrap.createChild({ tag: 'div' });
49313         if (style) {
49314             this.viewEl.applyStyles(style);
49315         }
49316         if (this.width) {
49317             this.viewEl.setWidth(this.width);
49318         }
49319         if (this.height) {
49320             this.viewEl.setHeight(this.height);
49321         }
49322         //if(this.inputValue !== undefined){
49323         //this.setValue(this.value);
49324         
49325         
49326         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49327         
49328         
49329         this.grid.render();
49330         this.grid.getDataSource().on('remove', this.refreshValue, this);
49331         this.grid.getDataSource().on('update', this.refreshValue, this);
49332         this.grid.on('afteredit', this.refreshValue, this);
49333  
49334     },
49335      
49336     
49337     /**
49338      * Sets the value of the item. 
49339      * @param {String} either an object  or a string..
49340      */
49341     setValue : function(v){
49342         //this.value = v;
49343         v = v || []; // empty set..
49344         // this does not seem smart - it really only affects memoryproxy grids..
49345         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49346             var ds = this.grid.getDataSource();
49347             // assumes a json reader..
49348             var data = {}
49349             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49350             ds.loadData( data);
49351         }
49352         // clear selection so it does not get stale.
49353         if (this.grid.sm) { 
49354             this.grid.sm.clearSelections();
49355         }
49356         
49357         Roo.form.GridField.superclass.setValue.call(this, v);
49358         this.refreshValue();
49359         // should load data in the grid really....
49360     },
49361     
49362     // private
49363     refreshValue: function() {
49364          var val = [];
49365         this.grid.getDataSource().each(function(r) {
49366             val.push(r.data);
49367         });
49368         this.el.dom.value = Roo.encode(val);
49369     }
49370     
49371      
49372     
49373     
49374 });/*
49375  * Based on:
49376  * Ext JS Library 1.1.1
49377  * Copyright(c) 2006-2007, Ext JS, LLC.
49378  *
49379  * Originally Released Under LGPL - original licence link has changed is not relivant.
49380  *
49381  * Fork - LGPL
49382  * <script type="text/javascript">
49383  */
49384 /**
49385  * @class Roo.form.DisplayField
49386  * @extends Roo.form.Field
49387  * A generic Field to display non-editable data.
49388  * @cfg {Boolean} closable (true|false) default false
49389  * @constructor
49390  * Creates a new Display Field item.
49391  * @param {Object} config Configuration options
49392  */
49393 Roo.form.DisplayField = function(config){
49394     Roo.form.DisplayField.superclass.constructor.call(this, config);
49395     
49396     this.addEvents({
49397         /**
49398          * @event close
49399          * Fires after the click the close btn
49400              * @param {Roo.form.DisplayField} this
49401              */
49402         close : true
49403     });
49404 };
49405
49406 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49407     inputType:      'hidden',
49408     allowBlank:     true,
49409     readOnly:         true,
49410     
49411  
49412     /**
49413      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49414      */
49415     focusClass : undefined,
49416     /**
49417      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49418      */
49419     fieldClass: 'x-form-field',
49420     
49421      /**
49422      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49423      */
49424     valueRenderer: undefined,
49425     
49426     width: 100,
49427     /**
49428      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49429      * {tag: "input", type: "checkbox", autocomplete: "off"})
49430      */
49431      
49432  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49433  
49434     closable : false,
49435     
49436     onResize : function(){
49437         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49438         
49439     },
49440
49441     initEvents : function(){
49442         // Roo.form.Checkbox.superclass.initEvents.call(this);
49443         // has no events...
49444         
49445         if(this.closable){
49446             this.closeEl.on('click', this.onClose, this);
49447         }
49448        
49449     },
49450
49451
49452     getResizeEl : function(){
49453         return this.wrap;
49454     },
49455
49456     getPositionEl : function(){
49457         return this.wrap;
49458     },
49459
49460     // private
49461     onRender : function(ct, position){
49462         
49463         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49464         //if(this.inputValue !== undefined){
49465         this.wrap = this.el.wrap();
49466         
49467         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49468         
49469         if(this.closable){
49470             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49471         }
49472         
49473         if (this.bodyStyle) {
49474             this.viewEl.applyStyles(this.bodyStyle);
49475         }
49476         //this.viewEl.setStyle('padding', '2px');
49477         
49478         this.setValue(this.value);
49479         
49480     },
49481 /*
49482     // private
49483     initValue : Roo.emptyFn,
49484
49485   */
49486
49487         // private
49488     onClick : function(){
49489         
49490     },
49491
49492     /**
49493      * Sets the checked state of the checkbox.
49494      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49495      */
49496     setValue : function(v){
49497         this.value = v;
49498         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49499         // this might be called before we have a dom element..
49500         if (!this.viewEl) {
49501             return;
49502         }
49503         this.viewEl.dom.innerHTML = html;
49504         Roo.form.DisplayField.superclass.setValue.call(this, v);
49505
49506     },
49507     
49508     onClose : function(e)
49509     {
49510         e.preventDefault();
49511         
49512         this.fireEvent('close', this);
49513     }
49514 });/*
49515  * 
49516  * Licence- LGPL
49517  * 
49518  */
49519
49520 /**
49521  * @class Roo.form.DayPicker
49522  * @extends Roo.form.Field
49523  * A Day picker show [M] [T] [W] ....
49524  * @constructor
49525  * Creates a new Day Picker
49526  * @param {Object} config Configuration options
49527  */
49528 Roo.form.DayPicker= function(config){
49529     Roo.form.DayPicker.superclass.constructor.call(this, config);
49530      
49531 };
49532
49533 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49534     /**
49535      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49536      */
49537     focusClass : undefined,
49538     /**
49539      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49540      */
49541     fieldClass: "x-form-field",
49542    
49543     /**
49544      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49545      * {tag: "input", type: "checkbox", autocomplete: "off"})
49546      */
49547     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49548     
49549    
49550     actionMode : 'viewEl', 
49551     //
49552     // private
49553  
49554     inputType : 'hidden',
49555     
49556      
49557     inputElement: false, // real input element?
49558     basedOn: false, // ????
49559     
49560     isFormField: true, // not sure where this is needed!!!!
49561
49562     onResize : function(){
49563         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49564         if(!this.boxLabel){
49565             this.el.alignTo(this.wrap, 'c-c');
49566         }
49567     },
49568
49569     initEvents : function(){
49570         Roo.form.Checkbox.superclass.initEvents.call(this);
49571         this.el.on("click", this.onClick,  this);
49572         this.el.on("change", this.onClick,  this);
49573     },
49574
49575
49576     getResizeEl : function(){
49577         return this.wrap;
49578     },
49579
49580     getPositionEl : function(){
49581         return this.wrap;
49582     },
49583
49584     
49585     // private
49586     onRender : function(ct, position){
49587         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49588        
49589         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49590         
49591         var r1 = '<table><tr>';
49592         var r2 = '<tr class="x-form-daypick-icons">';
49593         for (var i=0; i < 7; i++) {
49594             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49595             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49596         }
49597         
49598         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49599         viewEl.select('img').on('click', this.onClick, this);
49600         this.viewEl = viewEl;   
49601         
49602         
49603         // this will not work on Chrome!!!
49604         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49605         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49606         
49607         
49608           
49609
49610     },
49611
49612     // private
49613     initValue : Roo.emptyFn,
49614
49615     /**
49616      * Returns the checked state of the checkbox.
49617      * @return {Boolean} True if checked, else false
49618      */
49619     getValue : function(){
49620         return this.el.dom.value;
49621         
49622     },
49623
49624         // private
49625     onClick : function(e){ 
49626         //this.setChecked(!this.checked);
49627         Roo.get(e.target).toggleClass('x-menu-item-checked');
49628         this.refreshValue();
49629         //if(this.el.dom.checked != this.checked){
49630         //    this.setValue(this.el.dom.checked);
49631        // }
49632     },
49633     
49634     // private
49635     refreshValue : function()
49636     {
49637         var val = '';
49638         this.viewEl.select('img',true).each(function(e,i,n)  {
49639             val += e.is(".x-menu-item-checked") ? String(n) : '';
49640         });
49641         this.setValue(val, true);
49642     },
49643
49644     /**
49645      * Sets the checked state of the checkbox.
49646      * On is always based on a string comparison between inputValue and the param.
49647      * @param {Boolean/String} value - the value to set 
49648      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49649      */
49650     setValue : function(v,suppressEvent){
49651         if (!this.el.dom) {
49652             return;
49653         }
49654         var old = this.el.dom.value ;
49655         this.el.dom.value = v;
49656         if (suppressEvent) {
49657             return ;
49658         }
49659          
49660         // update display..
49661         this.viewEl.select('img',true).each(function(e,i,n)  {
49662             
49663             var on = e.is(".x-menu-item-checked");
49664             var newv = v.indexOf(String(n)) > -1;
49665             if (on != newv) {
49666                 e.toggleClass('x-menu-item-checked');
49667             }
49668             
49669         });
49670         
49671         
49672         this.fireEvent('change', this, v, old);
49673         
49674         
49675     },
49676    
49677     // handle setting of hidden value by some other method!!?!?
49678     setFromHidden: function()
49679     {
49680         if(!this.el){
49681             return;
49682         }
49683         //console.log("SET FROM HIDDEN");
49684         //alert('setFrom hidden');
49685         this.setValue(this.el.dom.value);
49686     },
49687     
49688     onDestroy : function()
49689     {
49690         if(this.viewEl){
49691             Roo.get(this.viewEl).remove();
49692         }
49693          
49694         Roo.form.DayPicker.superclass.onDestroy.call(this);
49695     }
49696
49697 });/*
49698  * RooJS Library 1.1.1
49699  * Copyright(c) 2008-2011  Alan Knowles
49700  *
49701  * License - LGPL
49702  */
49703  
49704
49705 /**
49706  * @class Roo.form.ComboCheck
49707  * @extends Roo.form.ComboBox
49708  * A combobox for multiple select items.
49709  *
49710  * FIXME - could do with a reset button..
49711  * 
49712  * @constructor
49713  * Create a new ComboCheck
49714  * @param {Object} config Configuration options
49715  */
49716 Roo.form.ComboCheck = function(config){
49717     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49718     // should verify some data...
49719     // like
49720     // hiddenName = required..
49721     // displayField = required
49722     // valudField == required
49723     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49724     var _t = this;
49725     Roo.each(req, function(e) {
49726         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49727             throw "Roo.form.ComboCheck : missing value for: " + e;
49728         }
49729     });
49730     
49731     
49732 };
49733
49734 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49735      
49736      
49737     editable : false,
49738      
49739     selectedClass: 'x-menu-item-checked', 
49740     
49741     // private
49742     onRender : function(ct, position){
49743         var _t = this;
49744         
49745         
49746         
49747         if(!this.tpl){
49748             var cls = 'x-combo-list';
49749
49750             
49751             this.tpl =  new Roo.Template({
49752                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49753                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49754                    '<span>{' + this.displayField + '}</span>' +
49755                     '</div>' 
49756                 
49757             });
49758         }
49759  
49760         
49761         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49762         this.view.singleSelect = false;
49763         this.view.multiSelect = true;
49764         this.view.toggleSelect = true;
49765         this.pageTb.add(new Roo.Toolbar.Fill(), {
49766             
49767             text: 'Done',
49768             handler: function()
49769             {
49770                 _t.collapse();
49771             }
49772         });
49773     },
49774     
49775     onViewOver : function(e, t){
49776         // do nothing...
49777         return;
49778         
49779     },
49780     
49781     onViewClick : function(doFocus,index){
49782         return;
49783         
49784     },
49785     select: function () {
49786         //Roo.log("SELECT CALLED");
49787     },
49788      
49789     selectByValue : function(xv, scrollIntoView){
49790         var ar = this.getValueArray();
49791         var sels = [];
49792         
49793         Roo.each(ar, function(v) {
49794             if(v === undefined || v === null){
49795                 return;
49796             }
49797             var r = this.findRecord(this.valueField, v);
49798             if(r){
49799                 sels.push(this.store.indexOf(r))
49800                 
49801             }
49802         },this);
49803         this.view.select(sels);
49804         return false;
49805     },
49806     
49807     
49808     
49809     onSelect : function(record, index){
49810        // Roo.log("onselect Called");
49811        // this is only called by the clear button now..
49812         this.view.clearSelections();
49813         this.setValue('[]');
49814         if (this.value != this.valueBefore) {
49815             this.fireEvent('change', this, this.value, this.valueBefore);
49816             this.valueBefore = this.value;
49817         }
49818     },
49819     getValueArray : function()
49820     {
49821         var ar = [] ;
49822         
49823         try {
49824             //Roo.log(this.value);
49825             if (typeof(this.value) == 'undefined') {
49826                 return [];
49827             }
49828             var ar = Roo.decode(this.value);
49829             return  ar instanceof Array ? ar : []; //?? valid?
49830             
49831         } catch(e) {
49832             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49833             return [];
49834         }
49835          
49836     },
49837     expand : function ()
49838     {
49839         
49840         Roo.form.ComboCheck.superclass.expand.call(this);
49841         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49842         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49843         
49844
49845     },
49846     
49847     collapse : function(){
49848         Roo.form.ComboCheck.superclass.collapse.call(this);
49849         var sl = this.view.getSelectedIndexes();
49850         var st = this.store;
49851         var nv = [];
49852         var tv = [];
49853         var r;
49854         Roo.each(sl, function(i) {
49855             r = st.getAt(i);
49856             nv.push(r.get(this.valueField));
49857         },this);
49858         this.setValue(Roo.encode(nv));
49859         if (this.value != this.valueBefore) {
49860
49861             this.fireEvent('change', this, this.value, this.valueBefore);
49862             this.valueBefore = this.value;
49863         }
49864         
49865     },
49866     
49867     setValue : function(v){
49868         // Roo.log(v);
49869         this.value = v;
49870         
49871         var vals = this.getValueArray();
49872         var tv = [];
49873         Roo.each(vals, function(k) {
49874             var r = this.findRecord(this.valueField, k);
49875             if(r){
49876                 tv.push(r.data[this.displayField]);
49877             }else if(this.valueNotFoundText !== undefined){
49878                 tv.push( this.valueNotFoundText );
49879             }
49880         },this);
49881        // Roo.log(tv);
49882         
49883         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49884         this.hiddenField.value = v;
49885         this.value = v;
49886     }
49887     
49888 });/*
49889  * Based on:
49890  * Ext JS Library 1.1.1
49891  * Copyright(c) 2006-2007, Ext JS, LLC.
49892  *
49893  * Originally Released Under LGPL - original licence link has changed is not relivant.
49894  *
49895  * Fork - LGPL
49896  * <script type="text/javascript">
49897  */
49898  
49899 /**
49900  * @class Roo.form.Signature
49901  * @extends Roo.form.Field
49902  * Signature field.  
49903  * @constructor
49904  * 
49905  * @param {Object} config Configuration options
49906  */
49907
49908 Roo.form.Signature = function(config){
49909     Roo.form.Signature.superclass.constructor.call(this, config);
49910     
49911     this.addEvents({// not in used??
49912          /**
49913          * @event confirm
49914          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49915              * @param {Roo.form.Signature} combo This combo box
49916              */
49917         'confirm' : true,
49918         /**
49919          * @event reset
49920          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49921              * @param {Roo.form.ComboBox} combo This combo box
49922              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49923              */
49924         'reset' : true
49925     });
49926 };
49927
49928 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49929     /**
49930      * @cfg {Object} labels Label to use when rendering a form.
49931      * defaults to 
49932      * labels : { 
49933      *      clear : "Clear",
49934      *      confirm : "Confirm"
49935      *  }
49936      */
49937     labels : { 
49938         clear : "Clear",
49939         confirm : "Confirm"
49940     },
49941     /**
49942      * @cfg {Number} width The signature panel width (defaults to 300)
49943      */
49944     width: 300,
49945     /**
49946      * @cfg {Number} height The signature panel height (defaults to 100)
49947      */
49948     height : 100,
49949     /**
49950      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49951      */
49952     allowBlank : false,
49953     
49954     //private
49955     // {Object} signPanel The signature SVG panel element (defaults to {})
49956     signPanel : {},
49957     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49958     isMouseDown : false,
49959     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49960     isConfirmed : false,
49961     // {String} signatureTmp SVG mapping string (defaults to empty string)
49962     signatureTmp : '',
49963     
49964     
49965     defaultAutoCreate : { // modified by initCompnoent..
49966         tag: "input",
49967         type:"hidden"
49968     },
49969
49970     // private
49971     onRender : function(ct, position){
49972         
49973         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49974         
49975         this.wrap = this.el.wrap({
49976             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49977         });
49978         
49979         this.createToolbar(this);
49980         this.signPanel = this.wrap.createChild({
49981                 tag: 'div',
49982                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49983             }, this.el
49984         );
49985             
49986         this.svgID = Roo.id();
49987         this.svgEl = this.signPanel.createChild({
49988               xmlns : 'http://www.w3.org/2000/svg',
49989               tag : 'svg',
49990               id : this.svgID + "-svg",
49991               width: this.width,
49992               height: this.height,
49993               viewBox: '0 0 '+this.width+' '+this.height,
49994               cn : [
49995                 {
49996                     tag: "rect",
49997                     id: this.svgID + "-svg-r",
49998                     width: this.width,
49999                     height: this.height,
50000                     fill: "#ffa"
50001                 },
50002                 {
50003                     tag: "line",
50004                     id: this.svgID + "-svg-l",
50005                     x1: "0", // start
50006                     y1: (this.height*0.8), // start set the line in 80% of height
50007                     x2: this.width, // end
50008                     y2: (this.height*0.8), // end set the line in 80% of height
50009                     'stroke': "#666",
50010                     'stroke-width': "1",
50011                     'stroke-dasharray': "3",
50012                     'shape-rendering': "crispEdges",
50013                     'pointer-events': "none"
50014                 },
50015                 {
50016                     tag: "path",
50017                     id: this.svgID + "-svg-p",
50018                     'stroke': "navy",
50019                     'stroke-width': "3",
50020                     'fill': "none",
50021                     'pointer-events': 'none'
50022                 }
50023               ]
50024         });
50025         this.createSVG();
50026         this.svgBox = this.svgEl.dom.getScreenCTM();
50027     },
50028     createSVG : function(){ 
50029         var svg = this.signPanel;
50030         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50031         var t = this;
50032
50033         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50034         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50035         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50036         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50037         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50038         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50039         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50040         
50041     },
50042     isTouchEvent : function(e){
50043         return e.type.match(/^touch/);
50044     },
50045     getCoords : function (e) {
50046         var pt    = this.svgEl.dom.createSVGPoint();
50047         pt.x = e.clientX; 
50048         pt.y = e.clientY;
50049         if (this.isTouchEvent(e)) {
50050             pt.x =  e.targetTouches[0].clientX;
50051             pt.y = e.targetTouches[0].clientY;
50052         }
50053         var a = this.svgEl.dom.getScreenCTM();
50054         var b = a.inverse();
50055         var mx = pt.matrixTransform(b);
50056         return mx.x + ',' + mx.y;
50057     },
50058     //mouse event headler 
50059     down : function (e) {
50060         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50061         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50062         
50063         this.isMouseDown = true;
50064         
50065         e.preventDefault();
50066     },
50067     move : function (e) {
50068         if (this.isMouseDown) {
50069             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50070             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50071         }
50072         
50073         e.preventDefault();
50074     },
50075     up : function (e) {
50076         this.isMouseDown = false;
50077         var sp = this.signatureTmp.split(' ');
50078         
50079         if(sp.length > 1){
50080             if(!sp[sp.length-2].match(/^L/)){
50081                 sp.pop();
50082                 sp.pop();
50083                 sp.push("");
50084                 this.signatureTmp = sp.join(" ");
50085             }
50086         }
50087         if(this.getValue() != this.signatureTmp){
50088             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50089             this.isConfirmed = false;
50090         }
50091         e.preventDefault();
50092     },
50093     
50094     /**
50095      * Protected method that will not generally be called directly. It
50096      * is called when the editor creates its toolbar. Override this method if you need to
50097      * add custom toolbar buttons.
50098      * @param {HtmlEditor} editor
50099      */
50100     createToolbar : function(editor){
50101          function btn(id, toggle, handler){
50102             var xid = fid + '-'+ id ;
50103             return {
50104                 id : xid,
50105                 cmd : id,
50106                 cls : 'x-btn-icon x-edit-'+id,
50107                 enableToggle:toggle !== false,
50108                 scope: editor, // was editor...
50109                 handler:handler||editor.relayBtnCmd,
50110                 clickEvent:'mousedown',
50111                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50112                 tabIndex:-1
50113             };
50114         }
50115         
50116         
50117         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50118         this.tb = tb;
50119         this.tb.add(
50120            {
50121                 cls : ' x-signature-btn x-signature-'+id,
50122                 scope: editor, // was editor...
50123                 handler: this.reset,
50124                 clickEvent:'mousedown',
50125                 text: this.labels.clear
50126             },
50127             {
50128                  xtype : 'Fill',
50129                  xns: Roo.Toolbar
50130             }, 
50131             {
50132                 cls : '  x-signature-btn x-signature-'+id,
50133                 scope: editor, // was editor...
50134                 handler: this.confirmHandler,
50135                 clickEvent:'mousedown',
50136                 text: this.labels.confirm
50137             }
50138         );
50139     
50140     },
50141     //public
50142     /**
50143      * when user is clicked confirm then show this image.....
50144      * 
50145      * @return {String} Image Data URI
50146      */
50147     getImageDataURI : function(){
50148         var svg = this.svgEl.dom.parentNode.innerHTML;
50149         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50150         return src; 
50151     },
50152     /**
50153      * 
50154      * @return {Boolean} this.isConfirmed
50155      */
50156     getConfirmed : function(){
50157         return this.isConfirmed;
50158     },
50159     /**
50160      * 
50161      * @return {Number} this.width
50162      */
50163     getWidth : function(){
50164         return this.width;
50165     },
50166     /**
50167      * 
50168      * @return {Number} this.height
50169      */
50170     getHeight : function(){
50171         return this.height;
50172     },
50173     // private
50174     getSignature : function(){
50175         return this.signatureTmp;
50176     },
50177     // private
50178     reset : function(){
50179         this.signatureTmp = '';
50180         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50181         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50182         this.isConfirmed = false;
50183         Roo.form.Signature.superclass.reset.call(this);
50184     },
50185     setSignature : function(s){
50186         this.signatureTmp = s;
50187         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50188         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50189         this.setValue(s);
50190         this.isConfirmed = false;
50191         Roo.form.Signature.superclass.reset.call(this);
50192     }, 
50193     test : function(){
50194 //        Roo.log(this.signPanel.dom.contentWindow.up())
50195     },
50196     //private
50197     setConfirmed : function(){
50198         
50199         
50200         
50201 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50202     },
50203     // private
50204     confirmHandler : function(){
50205         if(!this.getSignature()){
50206             return;
50207         }
50208         
50209         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50210         this.setValue(this.getSignature());
50211         this.isConfirmed = true;
50212         
50213         this.fireEvent('confirm', this);
50214     },
50215     // private
50216     // Subclasses should provide the validation implementation by overriding this
50217     validateValue : function(value){
50218         if(this.allowBlank){
50219             return true;
50220         }
50221         
50222         if(this.isConfirmed){
50223             return true;
50224         }
50225         return false;
50226     }
50227 });/*
50228  * Based on:
50229  * Ext JS Library 1.1.1
50230  * Copyright(c) 2006-2007, Ext JS, LLC.
50231  *
50232  * Originally Released Under LGPL - original licence link has changed is not relivant.
50233  *
50234  * Fork - LGPL
50235  * <script type="text/javascript">
50236  */
50237  
50238
50239 /**
50240  * @class Roo.form.ComboBox
50241  * @extends Roo.form.TriggerField
50242  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50243  * @constructor
50244  * Create a new ComboBox.
50245  * @param {Object} config Configuration options
50246  */
50247 Roo.form.Select = function(config){
50248     Roo.form.Select.superclass.constructor.call(this, config);
50249      
50250 };
50251
50252 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50253     /**
50254      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50255      */
50256     /**
50257      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50258      * rendering into an Roo.Editor, defaults to false)
50259      */
50260     /**
50261      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50262      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50263      */
50264     /**
50265      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50266      */
50267     /**
50268      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50269      * the dropdown list (defaults to undefined, with no header element)
50270      */
50271
50272      /**
50273      * @cfg {String/Roo.Template} tpl The template to use to render the output
50274      */
50275      
50276     // private
50277     defaultAutoCreate : {tag: "select"  },
50278     /**
50279      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50280      */
50281     listWidth: undefined,
50282     /**
50283      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50284      * mode = 'remote' or 'text' if mode = 'local')
50285      */
50286     displayField: undefined,
50287     /**
50288      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50289      * mode = 'remote' or 'value' if mode = 'local'). 
50290      * Note: use of a valueField requires the user make a selection
50291      * in order for a value to be mapped.
50292      */
50293     valueField: undefined,
50294     
50295     
50296     /**
50297      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50298      * field's data value (defaults to the underlying DOM element's name)
50299      */
50300     hiddenName: undefined,
50301     /**
50302      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50303      */
50304     listClass: '',
50305     /**
50306      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50307      */
50308     selectedClass: 'x-combo-selected',
50309     /**
50310      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50311      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50312      * which displays a downward arrow icon).
50313      */
50314     triggerClass : 'x-form-arrow-trigger',
50315     /**
50316      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50317      */
50318     shadow:'sides',
50319     /**
50320      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50321      * anchor positions (defaults to 'tl-bl')
50322      */
50323     listAlign: 'tl-bl?',
50324     /**
50325      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50326      */
50327     maxHeight: 300,
50328     /**
50329      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50330      * query specified by the allQuery config option (defaults to 'query')
50331      */
50332     triggerAction: 'query',
50333     /**
50334      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50335      * (defaults to 4, does not apply if editable = false)
50336      */
50337     minChars : 4,
50338     /**
50339      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50340      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50341      */
50342     typeAhead: false,
50343     /**
50344      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50345      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50346      */
50347     queryDelay: 500,
50348     /**
50349      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50350      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50351      */
50352     pageSize: 0,
50353     /**
50354      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50355      * when editable = true (defaults to false)
50356      */
50357     selectOnFocus:false,
50358     /**
50359      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50360      */
50361     queryParam: 'query',
50362     /**
50363      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50364      * when mode = 'remote' (defaults to 'Loading...')
50365      */
50366     loadingText: 'Loading...',
50367     /**
50368      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50369      */
50370     resizable: false,
50371     /**
50372      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50373      */
50374     handleHeight : 8,
50375     /**
50376      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50377      * traditional select (defaults to true)
50378      */
50379     editable: true,
50380     /**
50381      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50382      */
50383     allQuery: '',
50384     /**
50385      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50386      */
50387     mode: 'remote',
50388     /**
50389      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50390      * listWidth has a higher value)
50391      */
50392     minListWidth : 70,
50393     /**
50394      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50395      * allow the user to set arbitrary text into the field (defaults to false)
50396      */
50397     forceSelection:false,
50398     /**
50399      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50400      * if typeAhead = true (defaults to 250)
50401      */
50402     typeAheadDelay : 250,
50403     /**
50404      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50405      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50406      */
50407     valueNotFoundText : undefined,
50408     
50409     /**
50410      * @cfg {String} defaultValue The value displayed after loading the store.
50411      */
50412     defaultValue: '',
50413     
50414     /**
50415      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50416      */
50417     blockFocus : false,
50418     
50419     /**
50420      * @cfg {Boolean} disableClear Disable showing of clear button.
50421      */
50422     disableClear : false,
50423     /**
50424      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50425      */
50426     alwaysQuery : false,
50427     
50428     //private
50429     addicon : false,
50430     editicon: false,
50431     
50432     // element that contains real text value.. (when hidden is used..)
50433      
50434     // private
50435     onRender : function(ct, position){
50436         Roo.form.Field.prototype.onRender.call(this, ct, position);
50437         
50438         if(this.store){
50439             this.store.on('beforeload', this.onBeforeLoad, this);
50440             this.store.on('load', this.onLoad, this);
50441             this.store.on('loadexception', this.onLoadException, this);
50442             this.store.load({});
50443         }
50444         
50445         
50446         
50447     },
50448
50449     // private
50450     initEvents : function(){
50451         //Roo.form.ComboBox.superclass.initEvents.call(this);
50452  
50453     },
50454
50455     onDestroy : function(){
50456        
50457         if(this.store){
50458             this.store.un('beforeload', this.onBeforeLoad, this);
50459             this.store.un('load', this.onLoad, this);
50460             this.store.un('loadexception', this.onLoadException, this);
50461         }
50462         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50463     },
50464
50465     // private
50466     fireKey : function(e){
50467         if(e.isNavKeyPress() && !this.list.isVisible()){
50468             this.fireEvent("specialkey", this, e);
50469         }
50470     },
50471
50472     // private
50473     onResize: function(w, h){
50474         
50475         return; 
50476     
50477         
50478     },
50479
50480     /**
50481      * Allow or prevent the user from directly editing the field text.  If false is passed,
50482      * the user will only be able to select from the items defined in the dropdown list.  This method
50483      * is the runtime equivalent of setting the 'editable' config option at config time.
50484      * @param {Boolean} value True to allow the user to directly edit the field text
50485      */
50486     setEditable : function(value){
50487          
50488     },
50489
50490     // private
50491     onBeforeLoad : function(){
50492         
50493         Roo.log("Select before load");
50494         return;
50495     
50496         this.innerList.update(this.loadingText ?
50497                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50498         //this.restrictHeight();
50499         this.selectedIndex = -1;
50500     },
50501
50502     // private
50503     onLoad : function(){
50504
50505     
50506         var dom = this.el.dom;
50507         dom.innerHTML = '';
50508          var od = dom.ownerDocument;
50509          
50510         if (this.emptyText) {
50511             var op = od.createElement('option');
50512             op.setAttribute('value', '');
50513             op.innerHTML = String.format('{0}', this.emptyText);
50514             dom.appendChild(op);
50515         }
50516         if(this.store.getCount() > 0){
50517            
50518             var vf = this.valueField;
50519             var df = this.displayField;
50520             this.store.data.each(function(r) {
50521                 // which colmsn to use... testing - cdoe / title..
50522                 var op = od.createElement('option');
50523                 op.setAttribute('value', r.data[vf]);
50524                 op.innerHTML = String.format('{0}', r.data[df]);
50525                 dom.appendChild(op);
50526             });
50527             if (typeof(this.defaultValue != 'undefined')) {
50528                 this.setValue(this.defaultValue);
50529             }
50530             
50531              
50532         }else{
50533             //this.onEmptyResults();
50534         }
50535         //this.el.focus();
50536     },
50537     // private
50538     onLoadException : function()
50539     {
50540         dom.innerHTML = '';
50541             
50542         Roo.log("Select on load exception");
50543         return;
50544     
50545         this.collapse();
50546         Roo.log(this.store.reader.jsonData);
50547         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50548             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50549         }
50550         
50551         
50552     },
50553     // private
50554     onTypeAhead : function(){
50555          
50556     },
50557
50558     // private
50559     onSelect : function(record, index){
50560         Roo.log('on select?');
50561         return;
50562         if(this.fireEvent('beforeselect', this, record, index) !== false){
50563             this.setFromData(index > -1 ? record.data : false);
50564             this.collapse();
50565             this.fireEvent('select', this, record, index);
50566         }
50567     },
50568
50569     /**
50570      * Returns the currently selected field value or empty string if no value is set.
50571      * @return {String} value The selected value
50572      */
50573     getValue : function(){
50574         var dom = this.el.dom;
50575         this.value = dom.options[dom.selectedIndex].value;
50576         return this.value;
50577         
50578     },
50579
50580     /**
50581      * Clears any text/value currently set in the field
50582      */
50583     clearValue : function(){
50584         this.value = '';
50585         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50586         
50587     },
50588
50589     /**
50590      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50591      * will be displayed in the field.  If the value does not match the data value of an existing item,
50592      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50593      * Otherwise the field will be blank (although the value will still be set).
50594      * @param {String} value The value to match
50595      */
50596     setValue : function(v){
50597         var d = this.el.dom;
50598         for (var i =0; i < d.options.length;i++) {
50599             if (v == d.options[i].value) {
50600                 d.selectedIndex = i;
50601                 this.value = v;
50602                 return;
50603             }
50604         }
50605         this.clearValue();
50606     },
50607     /**
50608      * @property {Object} the last set data for the element
50609      */
50610     
50611     lastData : false,
50612     /**
50613      * Sets the value of the field based on a object which is related to the record format for the store.
50614      * @param {Object} value the value to set as. or false on reset?
50615      */
50616     setFromData : function(o){
50617         Roo.log('setfrom data?');
50618          
50619         
50620         
50621     },
50622     // private
50623     reset : function(){
50624         this.clearValue();
50625     },
50626     // private
50627     findRecord : function(prop, value){
50628         
50629         return false;
50630     
50631         var record;
50632         if(this.store.getCount() > 0){
50633             this.store.each(function(r){
50634                 if(r.data[prop] == value){
50635                     record = r;
50636                     return false;
50637                 }
50638                 return true;
50639             });
50640         }
50641         return record;
50642     },
50643     
50644     getName: function()
50645     {
50646         // returns hidden if it's set..
50647         if (!this.rendered) {return ''};
50648         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50649         
50650     },
50651      
50652
50653     
50654
50655     // private
50656     onEmptyResults : function(){
50657         Roo.log('empty results');
50658         //this.collapse();
50659     },
50660
50661     /**
50662      * Returns true if the dropdown list is expanded, else false.
50663      */
50664     isExpanded : function(){
50665         return false;
50666     },
50667
50668     /**
50669      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50670      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50671      * @param {String} value The data value of the item to select
50672      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50673      * selected item if it is not currently in view (defaults to true)
50674      * @return {Boolean} True if the value matched an item in the list, else false
50675      */
50676     selectByValue : function(v, scrollIntoView){
50677         Roo.log('select By Value');
50678         return false;
50679     
50680         if(v !== undefined && v !== null){
50681             var r = this.findRecord(this.valueField || this.displayField, v);
50682             if(r){
50683                 this.select(this.store.indexOf(r), scrollIntoView);
50684                 return true;
50685             }
50686         }
50687         return false;
50688     },
50689
50690     /**
50691      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50692      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50693      * @param {Number} index The zero-based index of the list item to select
50694      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50695      * selected item if it is not currently in view (defaults to true)
50696      */
50697     select : function(index, scrollIntoView){
50698         Roo.log('select ');
50699         return  ;
50700         
50701         this.selectedIndex = index;
50702         this.view.select(index);
50703         if(scrollIntoView !== false){
50704             var el = this.view.getNode(index);
50705             if(el){
50706                 this.innerList.scrollChildIntoView(el, false);
50707             }
50708         }
50709     },
50710
50711       
50712
50713     // private
50714     validateBlur : function(){
50715         
50716         return;
50717         
50718     },
50719
50720     // private
50721     initQuery : function(){
50722         this.doQuery(this.getRawValue());
50723     },
50724
50725     // private
50726     doForce : function(){
50727         if(this.el.dom.value.length > 0){
50728             this.el.dom.value =
50729                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50730              
50731         }
50732     },
50733
50734     /**
50735      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50736      * query allowing the query action to be canceled if needed.
50737      * @param {String} query The SQL query to execute
50738      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50739      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50740      * saved in the current store (defaults to false)
50741      */
50742     doQuery : function(q, forceAll){
50743         
50744         Roo.log('doQuery?');
50745         if(q === undefined || q === null){
50746             q = '';
50747         }
50748         var qe = {
50749             query: q,
50750             forceAll: forceAll,
50751             combo: this,
50752             cancel:false
50753         };
50754         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50755             return false;
50756         }
50757         q = qe.query;
50758         forceAll = qe.forceAll;
50759         if(forceAll === true || (q.length >= this.minChars)){
50760             if(this.lastQuery != q || this.alwaysQuery){
50761                 this.lastQuery = q;
50762                 if(this.mode == 'local'){
50763                     this.selectedIndex = -1;
50764                     if(forceAll){
50765                         this.store.clearFilter();
50766                     }else{
50767                         this.store.filter(this.displayField, q);
50768                     }
50769                     this.onLoad();
50770                 }else{
50771                     this.store.baseParams[this.queryParam] = q;
50772                     this.store.load({
50773                         params: this.getParams(q)
50774                     });
50775                     this.expand();
50776                 }
50777             }else{
50778                 this.selectedIndex = -1;
50779                 this.onLoad();   
50780             }
50781         }
50782     },
50783
50784     // private
50785     getParams : function(q){
50786         var p = {};
50787         //p[this.queryParam] = q;
50788         if(this.pageSize){
50789             p.start = 0;
50790             p.limit = this.pageSize;
50791         }
50792         return p;
50793     },
50794
50795     /**
50796      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50797      */
50798     collapse : function(){
50799         
50800     },
50801
50802     // private
50803     collapseIf : function(e){
50804         
50805     },
50806
50807     /**
50808      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50809      */
50810     expand : function(){
50811         
50812     } ,
50813
50814     // private
50815      
50816
50817     /** 
50818     * @cfg {Boolean} grow 
50819     * @hide 
50820     */
50821     /** 
50822     * @cfg {Number} growMin 
50823     * @hide 
50824     */
50825     /** 
50826     * @cfg {Number} growMax 
50827     * @hide 
50828     */
50829     /**
50830      * @hide
50831      * @method autoSize
50832      */
50833     
50834     setWidth : function()
50835     {
50836         
50837     },
50838     getResizeEl : function(){
50839         return this.el;
50840     }
50841 });//<script type="text/javasscript">
50842  
50843
50844 /**
50845  * @class Roo.DDView
50846  * A DnD enabled version of Roo.View.
50847  * @param {Element/String} container The Element in which to create the View.
50848  * @param {String} tpl The template string used to create the markup for each element of the View
50849  * @param {Object} config The configuration properties. These include all the config options of
50850  * {@link Roo.View} plus some specific to this class.<br>
50851  * <p>
50852  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50853  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50854  * <p>
50855  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50856 .x-view-drag-insert-above {
50857         border-top:1px dotted #3366cc;
50858 }
50859 .x-view-drag-insert-below {
50860         border-bottom:1px dotted #3366cc;
50861 }
50862 </code></pre>
50863  * 
50864  */
50865  
50866 Roo.DDView = function(container, tpl, config) {
50867     Roo.DDView.superclass.constructor.apply(this, arguments);
50868     this.getEl().setStyle("outline", "0px none");
50869     this.getEl().unselectable();
50870     if (this.dragGroup) {
50871                 this.setDraggable(this.dragGroup.split(","));
50872     }
50873     if (this.dropGroup) {
50874                 this.setDroppable(this.dropGroup.split(","));
50875     }
50876     if (this.deletable) {
50877         this.setDeletable();
50878     }
50879     this.isDirtyFlag = false;
50880         this.addEvents({
50881                 "drop" : true
50882         });
50883 };
50884
50885 Roo.extend(Roo.DDView, Roo.View, {
50886 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50887 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50888 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50889 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50890
50891         isFormField: true,
50892
50893         reset: Roo.emptyFn,
50894         
50895         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50896
50897         validate: function() {
50898                 return true;
50899         },
50900         
50901         destroy: function() {
50902                 this.purgeListeners();
50903                 this.getEl.removeAllListeners();
50904                 this.getEl().remove();
50905                 if (this.dragZone) {
50906                         if (this.dragZone.destroy) {
50907                                 this.dragZone.destroy();
50908                         }
50909                 }
50910                 if (this.dropZone) {
50911                         if (this.dropZone.destroy) {
50912                                 this.dropZone.destroy();
50913                         }
50914                 }
50915         },
50916
50917 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50918         getName: function() {
50919                 return this.name;
50920         },
50921
50922 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50923         setValue: function(v) {
50924                 if (!this.store) {
50925                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50926                 }
50927                 var data = {};
50928                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50929                 this.store.proxy = new Roo.data.MemoryProxy(data);
50930                 this.store.load();
50931         },
50932
50933 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50934         getValue: function() {
50935                 var result = '(';
50936                 this.store.each(function(rec) {
50937                         result += rec.id + ',';
50938                 });
50939                 return result.substr(0, result.length - 1) + ')';
50940         },
50941         
50942         getIds: function() {
50943                 var i = 0, result = new Array(this.store.getCount());
50944                 this.store.each(function(rec) {
50945                         result[i++] = rec.id;
50946                 });
50947                 return result;
50948         },
50949         
50950         isDirty: function() {
50951                 return this.isDirtyFlag;
50952         },
50953
50954 /**
50955  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50956  *      whole Element becomes the target, and this causes the drop gesture to append.
50957  */
50958     getTargetFromEvent : function(e) {
50959                 var target = e.getTarget();
50960                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50961                 target = target.parentNode;
50962                 }
50963                 if (!target) {
50964                         target = this.el.dom.lastChild || this.el.dom;
50965                 }
50966                 return target;
50967     },
50968
50969 /**
50970  *      Create the drag data which consists of an object which has the property "ddel" as
50971  *      the drag proxy element. 
50972  */
50973     getDragData : function(e) {
50974         var target = this.findItemFromChild(e.getTarget());
50975                 if(target) {
50976                         this.handleSelection(e);
50977                         var selNodes = this.getSelectedNodes();
50978             var dragData = {
50979                 source: this,
50980                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50981                 nodes: selNodes,
50982                 records: []
50983                         };
50984                         var selectedIndices = this.getSelectedIndexes();
50985                         for (var i = 0; i < selectedIndices.length; i++) {
50986                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50987                         }
50988                         if (selNodes.length == 1) {
50989                                 dragData.ddel = target.cloneNode(true); // the div element
50990                         } else {
50991                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50992                                 div.className = 'multi-proxy';
50993                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50994                                         div.appendChild(selNodes[i].cloneNode(true));
50995                                 }
50996                                 dragData.ddel = div;
50997                         }
50998             //console.log(dragData)
50999             //console.log(dragData.ddel.innerHTML)
51000                         return dragData;
51001                 }
51002         //console.log('nodragData')
51003                 return false;
51004     },
51005     
51006 /**     Specify to which ddGroup items in this DDView may be dragged. */
51007     setDraggable: function(ddGroup) {
51008         if (ddGroup instanceof Array) {
51009                 Roo.each(ddGroup, this.setDraggable, this);
51010                 return;
51011         }
51012         if (this.dragZone) {
51013                 this.dragZone.addToGroup(ddGroup);
51014         } else {
51015                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51016                                 containerScroll: true,
51017                                 ddGroup: ddGroup 
51018
51019                         });
51020 //                      Draggability implies selection. DragZone's mousedown selects the element.
51021                         if (!this.multiSelect) { this.singleSelect = true; }
51022
51023 //                      Wire the DragZone's handlers up to methods in *this*
51024                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51025                 }
51026     },
51027
51028 /**     Specify from which ddGroup this DDView accepts drops. */
51029     setDroppable: function(ddGroup) {
51030         if (ddGroup instanceof Array) {
51031                 Roo.each(ddGroup, this.setDroppable, this);
51032                 return;
51033         }
51034         if (this.dropZone) {
51035                 this.dropZone.addToGroup(ddGroup);
51036         } else {
51037                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51038                                 containerScroll: true,
51039                                 ddGroup: ddGroup
51040                         });
51041
51042 //                      Wire the DropZone's handlers up to methods in *this*
51043                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51044                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51045                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51046                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51047                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51048                 }
51049     },
51050
51051 /**     Decide whether to drop above or below a View node. */
51052     getDropPoint : function(e, n, dd){
51053         if (n == this.el.dom) { return "above"; }
51054                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51055                 var c = t + (b - t) / 2;
51056                 var y = Roo.lib.Event.getPageY(e);
51057                 if(y <= c) {
51058                         return "above";
51059                 }else{
51060                         return "below";
51061                 }
51062     },
51063
51064     onNodeEnter : function(n, dd, e, data){
51065                 return false;
51066     },
51067     
51068     onNodeOver : function(n, dd, e, data){
51069                 var pt = this.getDropPoint(e, n, dd);
51070                 // set the insert point style on the target node
51071                 var dragElClass = this.dropNotAllowed;
51072                 if (pt) {
51073                         var targetElClass;
51074                         if (pt == "above"){
51075                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51076                                 targetElClass = "x-view-drag-insert-above";
51077                         } else {
51078                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51079                                 targetElClass = "x-view-drag-insert-below";
51080                         }
51081                         if (this.lastInsertClass != targetElClass){
51082                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51083                                 this.lastInsertClass = targetElClass;
51084                         }
51085                 }
51086                 return dragElClass;
51087         },
51088
51089     onNodeOut : function(n, dd, e, data){
51090                 this.removeDropIndicators(n);
51091     },
51092
51093     onNodeDrop : function(n, dd, e, data){
51094         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51095                 return false;
51096         }
51097         var pt = this.getDropPoint(e, n, dd);
51098                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51099                 if (pt == "below") { insertAt++; }
51100                 for (var i = 0; i < data.records.length; i++) {
51101                         var r = data.records[i];
51102                         var dup = this.store.getById(r.id);
51103                         if (dup && (dd != this.dragZone)) {
51104                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51105                         } else {
51106                                 if (data.copy) {
51107                                         this.store.insert(insertAt++, r.copy());
51108                                 } else {
51109                                         data.source.isDirtyFlag = true;
51110                                         r.store.remove(r);
51111                                         this.store.insert(insertAt++, r);
51112                                 }
51113                                 this.isDirtyFlag = true;
51114                         }
51115                 }
51116                 this.dragZone.cachedTarget = null;
51117                 return true;
51118     },
51119
51120     removeDropIndicators : function(n){
51121                 if(n){
51122                         Roo.fly(n).removeClass([
51123                                 "x-view-drag-insert-above",
51124                                 "x-view-drag-insert-below"]);
51125                         this.lastInsertClass = "_noclass";
51126                 }
51127     },
51128
51129 /**
51130  *      Utility method. Add a delete option to the DDView's context menu.
51131  *      @param {String} imageUrl The URL of the "delete" icon image.
51132  */
51133         setDeletable: function(imageUrl) {
51134                 if (!this.singleSelect && !this.multiSelect) {
51135                         this.singleSelect = true;
51136                 }
51137                 var c = this.getContextMenu();
51138                 this.contextMenu.on("itemclick", function(item) {
51139                         switch (item.id) {
51140                                 case "delete":
51141                                         this.remove(this.getSelectedIndexes());
51142                                         break;
51143                         }
51144                 }, this);
51145                 this.contextMenu.add({
51146                         icon: imageUrl,
51147                         id: "delete",
51148                         text: 'Delete'
51149                 });
51150         },
51151         
51152 /**     Return the context menu for this DDView. */
51153         getContextMenu: function() {
51154                 if (!this.contextMenu) {
51155 //                      Create the View's context menu
51156                         this.contextMenu = new Roo.menu.Menu({
51157                                 id: this.id + "-contextmenu"
51158                         });
51159                         this.el.on("contextmenu", this.showContextMenu, this);
51160                 }
51161                 return this.contextMenu;
51162         },
51163         
51164         disableContextMenu: function() {
51165                 if (this.contextMenu) {
51166                         this.el.un("contextmenu", this.showContextMenu, this);
51167                 }
51168         },
51169
51170         showContextMenu: function(e, item) {
51171         item = this.findItemFromChild(e.getTarget());
51172                 if (item) {
51173                         e.stopEvent();
51174                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51175                         this.contextMenu.showAt(e.getXY());
51176             }
51177     },
51178
51179 /**
51180  *      Remove {@link Roo.data.Record}s at the specified indices.
51181  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51182  */
51183     remove: function(selectedIndices) {
51184                 selectedIndices = [].concat(selectedIndices);
51185                 for (var i = 0; i < selectedIndices.length; i++) {
51186                         var rec = this.store.getAt(selectedIndices[i]);
51187                         this.store.remove(rec);
51188                 }
51189     },
51190
51191 /**
51192  *      Double click fires the event, but also, if this is draggable, and there is only one other
51193  *      related DropZone, it transfers the selected node.
51194  */
51195     onDblClick : function(e){
51196         var item = this.findItemFromChild(e.getTarget());
51197         if(item){
51198             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51199                 return false;
51200             }
51201             if (this.dragGroup) {
51202                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51203                     while (targets.indexOf(this.dropZone) > -1) {
51204                             targets.remove(this.dropZone);
51205                                 }
51206                     if (targets.length == 1) {
51207                                         this.dragZone.cachedTarget = null;
51208                         var el = Roo.get(targets[0].getEl());
51209                         var box = el.getBox(true);
51210                         targets[0].onNodeDrop(el.dom, {
51211                                 target: el.dom,
51212                                 xy: [box.x, box.y + box.height - 1]
51213                         }, null, this.getDragData(e));
51214                     }
51215                 }
51216         }
51217     },
51218     
51219     handleSelection: function(e) {
51220                 this.dragZone.cachedTarget = null;
51221         var item = this.findItemFromChild(e.getTarget());
51222         if (!item) {
51223                 this.clearSelections(true);
51224                 return;
51225         }
51226                 if (item && (this.multiSelect || this.singleSelect)){
51227                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51228                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51229                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51230                                 this.unselect(item);
51231                         } else {
51232                                 this.select(item, this.multiSelect && e.ctrlKey);
51233                                 this.lastSelection = item;
51234                         }
51235                 }
51236     },
51237
51238     onItemClick : function(item, index, e){
51239                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51240                         return false;
51241                 }
51242                 return true;
51243     },
51244
51245     unselect : function(nodeInfo, suppressEvent){
51246                 var node = this.getNode(nodeInfo);
51247                 if(node && this.isSelected(node)){
51248                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51249                                 Roo.fly(node).removeClass(this.selectedClass);
51250                                 this.selections.remove(node);
51251                                 if(!suppressEvent){
51252                                         this.fireEvent("selectionchange", this, this.selections);
51253                                 }
51254                         }
51255                 }
51256     }
51257 });
51258 /*
51259  * Based on:
51260  * Ext JS Library 1.1.1
51261  * Copyright(c) 2006-2007, Ext JS, LLC.
51262  *
51263  * Originally Released Under LGPL - original licence link has changed is not relivant.
51264  *
51265  * Fork - LGPL
51266  * <script type="text/javascript">
51267  */
51268  
51269 /**
51270  * @class Roo.LayoutManager
51271  * @extends Roo.util.Observable
51272  * Base class for layout managers.
51273  */
51274 Roo.LayoutManager = function(container, config){
51275     Roo.LayoutManager.superclass.constructor.call(this);
51276     this.el = Roo.get(container);
51277     // ie scrollbar fix
51278     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51279         document.body.scroll = "no";
51280     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51281         this.el.position('relative');
51282     }
51283     this.id = this.el.id;
51284     this.el.addClass("x-layout-container");
51285     /** false to disable window resize monitoring @type Boolean */
51286     this.monitorWindowResize = true;
51287     this.regions = {};
51288     this.addEvents({
51289         /**
51290          * @event layout
51291          * Fires when a layout is performed. 
51292          * @param {Roo.LayoutManager} this
51293          */
51294         "layout" : true,
51295         /**
51296          * @event regionresized
51297          * Fires when the user resizes a region. 
51298          * @param {Roo.LayoutRegion} region The resized region
51299          * @param {Number} newSize The new size (width for east/west, height for north/south)
51300          */
51301         "regionresized" : true,
51302         /**
51303          * @event regioncollapsed
51304          * Fires when a region is collapsed. 
51305          * @param {Roo.LayoutRegion} region The collapsed region
51306          */
51307         "regioncollapsed" : true,
51308         /**
51309          * @event regionexpanded
51310          * Fires when a region is expanded.  
51311          * @param {Roo.LayoutRegion} region The expanded region
51312          */
51313         "regionexpanded" : true
51314     });
51315     this.updating = false;
51316     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51317 };
51318
51319 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51320     /**
51321      * Returns true if this layout is currently being updated
51322      * @return {Boolean}
51323      */
51324     isUpdating : function(){
51325         return this.updating; 
51326     },
51327     
51328     /**
51329      * Suspend the LayoutManager from doing auto-layouts while
51330      * making multiple add or remove calls
51331      */
51332     beginUpdate : function(){
51333         this.updating = true;    
51334     },
51335     
51336     /**
51337      * Restore auto-layouts and optionally disable the manager from performing a layout
51338      * @param {Boolean} noLayout true to disable a layout update 
51339      */
51340     endUpdate : function(noLayout){
51341         this.updating = false;
51342         if(!noLayout){
51343             this.layout();
51344         }    
51345     },
51346     
51347     layout: function(){
51348         
51349     },
51350     
51351     onRegionResized : function(region, newSize){
51352         this.fireEvent("regionresized", region, newSize);
51353         this.layout();
51354     },
51355     
51356     onRegionCollapsed : function(region){
51357         this.fireEvent("regioncollapsed", region);
51358     },
51359     
51360     onRegionExpanded : function(region){
51361         this.fireEvent("regionexpanded", region);
51362     },
51363         
51364     /**
51365      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51366      * performs box-model adjustments.
51367      * @return {Object} The size as an object {width: (the width), height: (the height)}
51368      */
51369     getViewSize : function(){
51370         var size;
51371         if(this.el.dom != document.body){
51372             size = this.el.getSize();
51373         }else{
51374             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51375         }
51376         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51377         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51378         return size;
51379     },
51380     
51381     /**
51382      * Returns the Element this layout is bound to.
51383      * @return {Roo.Element}
51384      */
51385     getEl : function(){
51386         return this.el;
51387     },
51388     
51389     /**
51390      * Returns the specified region.
51391      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51392      * @return {Roo.LayoutRegion}
51393      */
51394     getRegion : function(target){
51395         return this.regions[target.toLowerCase()];
51396     },
51397     
51398     onWindowResize : function(){
51399         if(this.monitorWindowResize){
51400             this.layout();
51401         }
51402     }
51403 });/*
51404  * Based on:
51405  * Ext JS Library 1.1.1
51406  * Copyright(c) 2006-2007, Ext JS, LLC.
51407  *
51408  * Originally Released Under LGPL - original licence link has changed is not relivant.
51409  *
51410  * Fork - LGPL
51411  * <script type="text/javascript">
51412  */
51413 /**
51414  * @class Roo.BorderLayout
51415  * @extends Roo.LayoutManager
51416  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51417  * please see: <br><br>
51418  * <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>
51419  * <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>
51420  * Example:
51421  <pre><code>
51422  var layout = new Roo.BorderLayout(document.body, {
51423     north: {
51424         initialSize: 25,
51425         titlebar: false
51426     },
51427     west: {
51428         split:true,
51429         initialSize: 200,
51430         minSize: 175,
51431         maxSize: 400,
51432         titlebar: true,
51433         collapsible: true
51434     },
51435     east: {
51436         split:true,
51437         initialSize: 202,
51438         minSize: 175,
51439         maxSize: 400,
51440         titlebar: true,
51441         collapsible: true
51442     },
51443     south: {
51444         split:true,
51445         initialSize: 100,
51446         minSize: 100,
51447         maxSize: 200,
51448         titlebar: true,
51449         collapsible: true
51450     },
51451     center: {
51452         titlebar: true,
51453         autoScroll:true,
51454         resizeTabs: true,
51455         minTabWidth: 50,
51456         preferredTabWidth: 150
51457     }
51458 });
51459
51460 // shorthand
51461 var CP = Roo.ContentPanel;
51462
51463 layout.beginUpdate();
51464 layout.add("north", new CP("north", "North"));
51465 layout.add("south", new CP("south", {title: "South", closable: true}));
51466 layout.add("west", new CP("west", {title: "West"}));
51467 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51468 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51469 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51470 layout.getRegion("center").showPanel("center1");
51471 layout.endUpdate();
51472 </code></pre>
51473
51474 <b>The container the layout is rendered into can be either the body element or any other element.
51475 If it is not the body element, the container needs to either be an absolute positioned element,
51476 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51477 the container size if it is not the body element.</b>
51478
51479 * @constructor
51480 * Create a new BorderLayout
51481 * @param {String/HTMLElement/Element} container The container this layout is bound to
51482 * @param {Object} config Configuration options
51483  */
51484 Roo.BorderLayout = function(container, config){
51485     config = config || {};
51486     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51487     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51488     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51489         var target = this.factory.validRegions[i];
51490         if(config[target]){
51491             this.addRegion(target, config[target]);
51492         }
51493     }
51494 };
51495
51496 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51497     /**
51498      * Creates and adds a new region if it doesn't already exist.
51499      * @param {String} target The target region key (north, south, east, west or center).
51500      * @param {Object} config The regions config object
51501      * @return {BorderLayoutRegion} The new region
51502      */
51503     addRegion : function(target, config){
51504         if(!this.regions[target]){
51505             var r = this.factory.create(target, this, config);
51506             this.bindRegion(target, r);
51507         }
51508         return this.regions[target];
51509     },
51510
51511     // private (kinda)
51512     bindRegion : function(name, r){
51513         this.regions[name] = r;
51514         r.on("visibilitychange", this.layout, this);
51515         r.on("paneladded", this.layout, this);
51516         r.on("panelremoved", this.layout, this);
51517         r.on("invalidated", this.layout, this);
51518         r.on("resized", this.onRegionResized, this);
51519         r.on("collapsed", this.onRegionCollapsed, this);
51520         r.on("expanded", this.onRegionExpanded, this);
51521     },
51522
51523     /**
51524      * Performs a layout update.
51525      */
51526     layout : function(){
51527         if(this.updating) {
51528             return;
51529         }
51530         var size = this.getViewSize();
51531         var w = size.width;
51532         var h = size.height;
51533         var centerW = w;
51534         var centerH = h;
51535         var centerY = 0;
51536         var centerX = 0;
51537         //var x = 0, y = 0;
51538
51539         var rs = this.regions;
51540         var north = rs["north"];
51541         var south = rs["south"]; 
51542         var west = rs["west"];
51543         var east = rs["east"];
51544         var center = rs["center"];
51545         //if(this.hideOnLayout){ // not supported anymore
51546             //c.el.setStyle("display", "none");
51547         //}
51548         if(north && north.isVisible()){
51549             var b = north.getBox();
51550             var m = north.getMargins();
51551             b.width = w - (m.left+m.right);
51552             b.x = m.left;
51553             b.y = m.top;
51554             centerY = b.height + b.y + m.bottom;
51555             centerH -= centerY;
51556             north.updateBox(this.safeBox(b));
51557         }
51558         if(south && south.isVisible()){
51559             var b = south.getBox();
51560             var m = south.getMargins();
51561             b.width = w - (m.left+m.right);
51562             b.x = m.left;
51563             var totalHeight = (b.height + m.top + m.bottom);
51564             b.y = h - totalHeight + m.top;
51565             centerH -= totalHeight;
51566             south.updateBox(this.safeBox(b));
51567         }
51568         if(west && west.isVisible()){
51569             var b = west.getBox();
51570             var m = west.getMargins();
51571             b.height = centerH - (m.top+m.bottom);
51572             b.x = m.left;
51573             b.y = centerY + m.top;
51574             var totalWidth = (b.width + m.left + m.right);
51575             centerX += totalWidth;
51576             centerW -= totalWidth;
51577             west.updateBox(this.safeBox(b));
51578         }
51579         if(east && east.isVisible()){
51580             var b = east.getBox();
51581             var m = east.getMargins();
51582             b.height = centerH - (m.top+m.bottom);
51583             var totalWidth = (b.width + m.left + m.right);
51584             b.x = w - totalWidth + m.left;
51585             b.y = centerY + m.top;
51586             centerW -= totalWidth;
51587             east.updateBox(this.safeBox(b));
51588         }
51589         if(center){
51590             var m = center.getMargins();
51591             var centerBox = {
51592                 x: centerX + m.left,
51593                 y: centerY + m.top,
51594                 width: centerW - (m.left+m.right),
51595                 height: centerH - (m.top+m.bottom)
51596             };
51597             //if(this.hideOnLayout){
51598                 //center.el.setStyle("display", "block");
51599             //}
51600             center.updateBox(this.safeBox(centerBox));
51601         }
51602         this.el.repaint();
51603         this.fireEvent("layout", this);
51604     },
51605
51606     // private
51607     safeBox : function(box){
51608         box.width = Math.max(0, box.width);
51609         box.height = Math.max(0, box.height);
51610         return box;
51611     },
51612
51613     /**
51614      * Adds a ContentPanel (or subclass) to this layout.
51615      * @param {String} target The target region key (north, south, east, west or center).
51616      * @param {Roo.ContentPanel} panel The panel to add
51617      * @return {Roo.ContentPanel} The added panel
51618      */
51619     add : function(target, panel){
51620          
51621         target = target.toLowerCase();
51622         return this.regions[target].add(panel);
51623     },
51624
51625     /**
51626      * Remove a ContentPanel (or subclass) to this layout.
51627      * @param {String} target The target region key (north, south, east, west or center).
51628      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51629      * @return {Roo.ContentPanel} The removed panel
51630      */
51631     remove : function(target, panel){
51632         target = target.toLowerCase();
51633         return this.regions[target].remove(panel);
51634     },
51635
51636     /**
51637      * Searches all regions for a panel with the specified id
51638      * @param {String} panelId
51639      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51640      */
51641     findPanel : function(panelId){
51642         var rs = this.regions;
51643         for(var target in rs){
51644             if(typeof rs[target] != "function"){
51645                 var p = rs[target].getPanel(panelId);
51646                 if(p){
51647                     return p;
51648                 }
51649             }
51650         }
51651         return null;
51652     },
51653
51654     /**
51655      * Searches all regions for a panel with the specified id and activates (shows) it.
51656      * @param {String/ContentPanel} panelId The panels id or the panel itself
51657      * @return {Roo.ContentPanel} The shown panel or null
51658      */
51659     showPanel : function(panelId) {
51660       var rs = this.regions;
51661       for(var target in rs){
51662          var r = rs[target];
51663          if(typeof r != "function"){
51664             if(r.hasPanel(panelId)){
51665                return r.showPanel(panelId);
51666             }
51667          }
51668       }
51669       return null;
51670    },
51671
51672    /**
51673      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51674      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51675      */
51676     restoreState : function(provider){
51677         if(!provider){
51678             provider = Roo.state.Manager;
51679         }
51680         var sm = new Roo.LayoutStateManager();
51681         sm.init(this, provider);
51682     },
51683
51684     /**
51685      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51686      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51687      * a valid ContentPanel config object.  Example:
51688      * <pre><code>
51689 // Create the main layout
51690 var layout = new Roo.BorderLayout('main-ct', {
51691     west: {
51692         split:true,
51693         minSize: 175,
51694         titlebar: true
51695     },
51696     center: {
51697         title:'Components'
51698     }
51699 }, 'main-ct');
51700
51701 // Create and add multiple ContentPanels at once via configs
51702 layout.batchAdd({
51703    west: {
51704        id: 'source-files',
51705        autoCreate:true,
51706        title:'Ext Source Files',
51707        autoScroll:true,
51708        fitToFrame:true
51709    },
51710    center : {
51711        el: cview,
51712        autoScroll:true,
51713        fitToFrame:true,
51714        toolbar: tb,
51715        resizeEl:'cbody'
51716    }
51717 });
51718 </code></pre>
51719      * @param {Object} regions An object containing ContentPanel configs by region name
51720      */
51721     batchAdd : function(regions){
51722         this.beginUpdate();
51723         for(var rname in regions){
51724             var lr = this.regions[rname];
51725             if(lr){
51726                 this.addTypedPanels(lr, regions[rname]);
51727             }
51728         }
51729         this.endUpdate();
51730     },
51731
51732     // private
51733     addTypedPanels : function(lr, ps){
51734         if(typeof ps == 'string'){
51735             lr.add(new Roo.ContentPanel(ps));
51736         }
51737         else if(ps instanceof Array){
51738             for(var i =0, len = ps.length; i < len; i++){
51739                 this.addTypedPanels(lr, ps[i]);
51740             }
51741         }
51742         else if(!ps.events){ // raw config?
51743             var el = ps.el;
51744             delete ps.el; // prevent conflict
51745             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51746         }
51747         else {  // panel object assumed!
51748             lr.add(ps);
51749         }
51750     },
51751     /**
51752      * Adds a xtype elements to the layout.
51753      * <pre><code>
51754
51755 layout.addxtype({
51756        xtype : 'ContentPanel',
51757        region: 'west',
51758        items: [ .... ]
51759    }
51760 );
51761
51762 layout.addxtype({
51763         xtype : 'NestedLayoutPanel',
51764         region: 'west',
51765         layout: {
51766            center: { },
51767            west: { }   
51768         },
51769         items : [ ... list of content panels or nested layout panels.. ]
51770    }
51771 );
51772 </code></pre>
51773      * @param {Object} cfg Xtype definition of item to add.
51774      */
51775     addxtype : function(cfg)
51776     {
51777         // basically accepts a pannel...
51778         // can accept a layout region..!?!?
51779         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51780         
51781         if (!cfg.xtype.match(/Panel$/)) {
51782             return false;
51783         }
51784         var ret = false;
51785         
51786         if (typeof(cfg.region) == 'undefined') {
51787             Roo.log("Failed to add Panel, region was not set");
51788             Roo.log(cfg);
51789             return false;
51790         }
51791         var region = cfg.region;
51792         delete cfg.region;
51793         
51794           
51795         var xitems = [];
51796         if (cfg.items) {
51797             xitems = cfg.items;
51798             delete cfg.items;
51799         }
51800         var nb = false;
51801         
51802         switch(cfg.xtype) 
51803         {
51804             case 'ContentPanel':  // ContentPanel (el, cfg)
51805             case 'ScrollPanel':  // ContentPanel (el, cfg)
51806             case 'ViewPanel': 
51807                 if(cfg.autoCreate) {
51808                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51809                 } else {
51810                     var el = this.el.createChild();
51811                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51812                 }
51813                 
51814                 this.add(region, ret);
51815                 break;
51816             
51817             
51818             case 'TreePanel': // our new panel!
51819                 cfg.el = this.el.createChild();
51820                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51821                 this.add(region, ret);
51822                 break;
51823             
51824             case 'NestedLayoutPanel': 
51825                 // create a new Layout (which is  a Border Layout...
51826                 var el = this.el.createChild();
51827                 var clayout = cfg.layout;
51828                 delete cfg.layout;
51829                 clayout.items   = clayout.items  || [];
51830                 // replace this exitems with the clayout ones..
51831                 xitems = clayout.items;
51832                  
51833                 
51834                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51835                     cfg.background = false;
51836                 }
51837                 var layout = new Roo.BorderLayout(el, clayout);
51838                 
51839                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51840                 //console.log('adding nested layout panel '  + cfg.toSource());
51841                 this.add(region, ret);
51842                 nb = {}; /// find first...
51843                 break;
51844                 
51845             case 'GridPanel': 
51846             
51847                 // needs grid and region
51848                 
51849                 //var el = this.getRegion(region).el.createChild();
51850                 var el = this.el.createChild();
51851                 // create the grid first...
51852                 
51853                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51854                 delete cfg.grid;
51855                 if (region == 'center' && this.active ) {
51856                     cfg.background = false;
51857                 }
51858                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51859                 
51860                 this.add(region, ret);
51861                 if (cfg.background) {
51862                     ret.on('activate', function(gp) {
51863                         if (!gp.grid.rendered) {
51864                             gp.grid.render();
51865                         }
51866                     });
51867                 } else {
51868                     grid.render();
51869                 }
51870                 break;
51871            
51872            
51873            
51874                 
51875                 
51876                 
51877             default:
51878                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51879                     
51880                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51881                     this.add(region, ret);
51882                 } else {
51883                 
51884                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51885                     return null;
51886                 }
51887                 
51888              // GridPanel (grid, cfg)
51889             
51890         }
51891         this.beginUpdate();
51892         // add children..
51893         var region = '';
51894         var abn = {};
51895         Roo.each(xitems, function(i)  {
51896             region = nb && i.region ? i.region : false;
51897             
51898             var add = ret.addxtype(i);
51899            
51900             if (region) {
51901                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51902                 if (!i.background) {
51903                     abn[region] = nb[region] ;
51904                 }
51905             }
51906             
51907         });
51908         this.endUpdate();
51909
51910         // make the last non-background panel active..
51911         //if (nb) { Roo.log(abn); }
51912         if (nb) {
51913             
51914             for(var r in abn) {
51915                 region = this.getRegion(r);
51916                 if (region) {
51917                     // tried using nb[r], but it does not work..
51918                      
51919                     region.showPanel(abn[r]);
51920                    
51921                 }
51922             }
51923         }
51924         return ret;
51925         
51926     }
51927 });
51928
51929 /**
51930  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51931  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51932  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51933  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51934  * <pre><code>
51935 // shorthand
51936 var CP = Roo.ContentPanel;
51937
51938 var layout = Roo.BorderLayout.create({
51939     north: {
51940         initialSize: 25,
51941         titlebar: false,
51942         panels: [new CP("north", "North")]
51943     },
51944     west: {
51945         split:true,
51946         initialSize: 200,
51947         minSize: 175,
51948         maxSize: 400,
51949         titlebar: true,
51950         collapsible: true,
51951         panels: [new CP("west", {title: "West"})]
51952     },
51953     east: {
51954         split:true,
51955         initialSize: 202,
51956         minSize: 175,
51957         maxSize: 400,
51958         titlebar: true,
51959         collapsible: true,
51960         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51961     },
51962     south: {
51963         split:true,
51964         initialSize: 100,
51965         minSize: 100,
51966         maxSize: 200,
51967         titlebar: true,
51968         collapsible: true,
51969         panels: [new CP("south", {title: "South", closable: true})]
51970     },
51971     center: {
51972         titlebar: true,
51973         autoScroll:true,
51974         resizeTabs: true,
51975         minTabWidth: 50,
51976         preferredTabWidth: 150,
51977         panels: [
51978             new CP("center1", {title: "Close Me", closable: true}),
51979             new CP("center2", {title: "Center Panel", closable: false})
51980         ]
51981     }
51982 }, document.body);
51983
51984 layout.getRegion("center").showPanel("center1");
51985 </code></pre>
51986  * @param config
51987  * @param targetEl
51988  */
51989 Roo.BorderLayout.create = function(config, targetEl){
51990     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51991     layout.beginUpdate();
51992     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51993     for(var j = 0, jlen = regions.length; j < jlen; j++){
51994         var lr = regions[j];
51995         if(layout.regions[lr] && config[lr].panels){
51996             var r = layout.regions[lr];
51997             var ps = config[lr].panels;
51998             layout.addTypedPanels(r, ps);
51999         }
52000     }
52001     layout.endUpdate();
52002     return layout;
52003 };
52004
52005 // private
52006 Roo.BorderLayout.RegionFactory = {
52007     // private
52008     validRegions : ["north","south","east","west","center"],
52009
52010     // private
52011     create : function(target, mgr, config){
52012         target = target.toLowerCase();
52013         if(config.lightweight || config.basic){
52014             return new Roo.BasicLayoutRegion(mgr, config, target);
52015         }
52016         switch(target){
52017             case "north":
52018                 return new Roo.NorthLayoutRegion(mgr, config);
52019             case "south":
52020                 return new Roo.SouthLayoutRegion(mgr, config);
52021             case "east":
52022                 return new Roo.EastLayoutRegion(mgr, config);
52023             case "west":
52024                 return new Roo.WestLayoutRegion(mgr, config);
52025             case "center":
52026                 return new Roo.CenterLayoutRegion(mgr, config);
52027         }
52028         throw 'Layout region "'+target+'" not supported.';
52029     }
52030 };/*
52031  * Based on:
52032  * Ext JS Library 1.1.1
52033  * Copyright(c) 2006-2007, Ext JS, LLC.
52034  *
52035  * Originally Released Under LGPL - original licence link has changed is not relivant.
52036  *
52037  * Fork - LGPL
52038  * <script type="text/javascript">
52039  */
52040  
52041 /**
52042  * @class Roo.BasicLayoutRegion
52043  * @extends Roo.util.Observable
52044  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52045  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52046  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52047  */
52048 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52049     this.mgr = mgr;
52050     this.position  = pos;
52051     this.events = {
52052         /**
52053          * @scope Roo.BasicLayoutRegion
52054          */
52055         
52056         /**
52057          * @event beforeremove
52058          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52059          * @param {Roo.LayoutRegion} this
52060          * @param {Roo.ContentPanel} panel The panel
52061          * @param {Object} e The cancel event object
52062          */
52063         "beforeremove" : true,
52064         /**
52065          * @event invalidated
52066          * Fires when the layout for this region is changed.
52067          * @param {Roo.LayoutRegion} this
52068          */
52069         "invalidated" : true,
52070         /**
52071          * @event visibilitychange
52072          * Fires when this region is shown or hidden 
52073          * @param {Roo.LayoutRegion} this
52074          * @param {Boolean} visibility true or false
52075          */
52076         "visibilitychange" : true,
52077         /**
52078          * @event paneladded
52079          * Fires when a panel is added. 
52080          * @param {Roo.LayoutRegion} this
52081          * @param {Roo.ContentPanel} panel The panel
52082          */
52083         "paneladded" : true,
52084         /**
52085          * @event panelremoved
52086          * Fires when a panel is removed. 
52087          * @param {Roo.LayoutRegion} this
52088          * @param {Roo.ContentPanel} panel The panel
52089          */
52090         "panelremoved" : true,
52091         /**
52092          * @event beforecollapse
52093          * Fires when this region before collapse.
52094          * @param {Roo.LayoutRegion} this
52095          */
52096         "beforecollapse" : true,
52097         /**
52098          * @event collapsed
52099          * Fires when this region is collapsed.
52100          * @param {Roo.LayoutRegion} this
52101          */
52102         "collapsed" : true,
52103         /**
52104          * @event expanded
52105          * Fires when this region is expanded.
52106          * @param {Roo.LayoutRegion} this
52107          */
52108         "expanded" : true,
52109         /**
52110          * @event slideshow
52111          * Fires when this region is slid into view.
52112          * @param {Roo.LayoutRegion} this
52113          */
52114         "slideshow" : true,
52115         /**
52116          * @event slidehide
52117          * Fires when this region slides out of view. 
52118          * @param {Roo.LayoutRegion} this
52119          */
52120         "slidehide" : true,
52121         /**
52122          * @event panelactivated
52123          * Fires when a panel is activated. 
52124          * @param {Roo.LayoutRegion} this
52125          * @param {Roo.ContentPanel} panel The activated panel
52126          */
52127         "panelactivated" : true,
52128         /**
52129          * @event resized
52130          * Fires when the user resizes this region. 
52131          * @param {Roo.LayoutRegion} this
52132          * @param {Number} newSize The new size (width for east/west, height for north/south)
52133          */
52134         "resized" : true
52135     };
52136     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52137     this.panels = new Roo.util.MixedCollection();
52138     this.panels.getKey = this.getPanelId.createDelegate(this);
52139     this.box = null;
52140     this.activePanel = null;
52141     // ensure listeners are added...
52142     
52143     if (config.listeners || config.events) {
52144         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52145             listeners : config.listeners || {},
52146             events : config.events || {}
52147         });
52148     }
52149     
52150     if(skipConfig !== true){
52151         this.applyConfig(config);
52152     }
52153 };
52154
52155 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52156     getPanelId : function(p){
52157         return p.getId();
52158     },
52159     
52160     applyConfig : function(config){
52161         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52162         this.config = config;
52163         
52164     },
52165     
52166     /**
52167      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52168      * the width, for horizontal (north, south) the height.
52169      * @param {Number} newSize The new width or height
52170      */
52171     resizeTo : function(newSize){
52172         var el = this.el ? this.el :
52173                  (this.activePanel ? this.activePanel.getEl() : null);
52174         if(el){
52175             switch(this.position){
52176                 case "east":
52177                 case "west":
52178                     el.setWidth(newSize);
52179                     this.fireEvent("resized", this, newSize);
52180                 break;
52181                 case "north":
52182                 case "south":
52183                     el.setHeight(newSize);
52184                     this.fireEvent("resized", this, newSize);
52185                 break;                
52186             }
52187         }
52188     },
52189     
52190     getBox : function(){
52191         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52192     },
52193     
52194     getMargins : function(){
52195         return this.margins;
52196     },
52197     
52198     updateBox : function(box){
52199         this.box = box;
52200         var el = this.activePanel.getEl();
52201         el.dom.style.left = box.x + "px";
52202         el.dom.style.top = box.y + "px";
52203         this.activePanel.setSize(box.width, box.height);
52204     },
52205     
52206     /**
52207      * Returns the container element for this region.
52208      * @return {Roo.Element}
52209      */
52210     getEl : function(){
52211         return this.activePanel;
52212     },
52213     
52214     /**
52215      * Returns true if this region is currently visible.
52216      * @return {Boolean}
52217      */
52218     isVisible : function(){
52219         return this.activePanel ? true : false;
52220     },
52221     
52222     setActivePanel : function(panel){
52223         panel = this.getPanel(panel);
52224         if(this.activePanel && this.activePanel != panel){
52225             this.activePanel.setActiveState(false);
52226             this.activePanel.getEl().setLeftTop(-10000,-10000);
52227         }
52228         this.activePanel = panel;
52229         panel.setActiveState(true);
52230         if(this.box){
52231             panel.setSize(this.box.width, this.box.height);
52232         }
52233         this.fireEvent("panelactivated", this, panel);
52234         this.fireEvent("invalidated");
52235     },
52236     
52237     /**
52238      * Show the specified panel.
52239      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52240      * @return {Roo.ContentPanel} The shown panel or null
52241      */
52242     showPanel : function(panel){
52243         if(panel = this.getPanel(panel)){
52244             this.setActivePanel(panel);
52245         }
52246         return panel;
52247     },
52248     
52249     /**
52250      * Get the active panel for this region.
52251      * @return {Roo.ContentPanel} The active panel or null
52252      */
52253     getActivePanel : function(){
52254         return this.activePanel;
52255     },
52256     
52257     /**
52258      * Add the passed ContentPanel(s)
52259      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52260      * @return {Roo.ContentPanel} The panel added (if only one was added)
52261      */
52262     add : function(panel){
52263         if(arguments.length > 1){
52264             for(var i = 0, len = arguments.length; i < len; i++) {
52265                 this.add(arguments[i]);
52266             }
52267             return null;
52268         }
52269         if(this.hasPanel(panel)){
52270             this.showPanel(panel);
52271             return panel;
52272         }
52273         var el = panel.getEl();
52274         if(el.dom.parentNode != this.mgr.el.dom){
52275             this.mgr.el.dom.appendChild(el.dom);
52276         }
52277         if(panel.setRegion){
52278             panel.setRegion(this);
52279         }
52280         this.panels.add(panel);
52281         el.setStyle("position", "absolute");
52282         if(!panel.background){
52283             this.setActivePanel(panel);
52284             if(this.config.initialSize && this.panels.getCount()==1){
52285                 this.resizeTo(this.config.initialSize);
52286             }
52287         }
52288         this.fireEvent("paneladded", this, panel);
52289         return panel;
52290     },
52291     
52292     /**
52293      * Returns true if the panel is in this region.
52294      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52295      * @return {Boolean}
52296      */
52297     hasPanel : function(panel){
52298         if(typeof panel == "object"){ // must be panel obj
52299             panel = panel.getId();
52300         }
52301         return this.getPanel(panel) ? true : false;
52302     },
52303     
52304     /**
52305      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52306      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52307      * @param {Boolean} preservePanel Overrides the config preservePanel option
52308      * @return {Roo.ContentPanel} The panel that was removed
52309      */
52310     remove : function(panel, preservePanel){
52311         panel = this.getPanel(panel);
52312         if(!panel){
52313             return null;
52314         }
52315         var e = {};
52316         this.fireEvent("beforeremove", this, panel, e);
52317         if(e.cancel === true){
52318             return null;
52319         }
52320         var panelId = panel.getId();
52321         this.panels.removeKey(panelId);
52322         return panel;
52323     },
52324     
52325     /**
52326      * Returns the panel specified or null if it's not in this region.
52327      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52328      * @return {Roo.ContentPanel}
52329      */
52330     getPanel : function(id){
52331         if(typeof id == "object"){ // must be panel obj
52332             return id;
52333         }
52334         return this.panels.get(id);
52335     },
52336     
52337     /**
52338      * Returns this regions position (north/south/east/west/center).
52339      * @return {String} 
52340      */
52341     getPosition: function(){
52342         return this.position;    
52343     }
52344 });/*
52345  * Based on:
52346  * Ext JS Library 1.1.1
52347  * Copyright(c) 2006-2007, Ext JS, LLC.
52348  *
52349  * Originally Released Under LGPL - original licence link has changed is not relivant.
52350  *
52351  * Fork - LGPL
52352  * <script type="text/javascript">
52353  */
52354  
52355 /**
52356  * @class Roo.LayoutRegion
52357  * @extends Roo.BasicLayoutRegion
52358  * This class represents a region in a layout manager.
52359  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52360  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52361  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52362  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52363  * @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})
52364  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52365  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52366  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52367  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52368  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52369  * @cfg {String}    title           The title for the region (overrides panel titles)
52370  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52371  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52372  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52373  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52374  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52375  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52376  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52377  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52378  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52379  * @cfg {Boolean}   showPin         True to show a pin button
52380  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52381  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52382  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52383  * @cfg {Number}    width           For East/West panels
52384  * @cfg {Number}    height          For North/South panels
52385  * @cfg {Boolean}   split           To show the splitter
52386  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52387  */
52388 Roo.LayoutRegion = function(mgr, config, pos){
52389     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52390     var dh = Roo.DomHelper;
52391     /** This region's container element 
52392     * @type Roo.Element */
52393     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52394     /** This region's title element 
52395     * @type Roo.Element */
52396
52397     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52398         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52399         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52400     ]}, true);
52401     this.titleEl.enableDisplayMode();
52402     /** This region's title text element 
52403     * @type HTMLElement */
52404     this.titleTextEl = this.titleEl.dom.firstChild;
52405     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52406     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52407     this.closeBtn.enableDisplayMode();
52408     this.closeBtn.on("click", this.closeClicked, this);
52409     this.closeBtn.hide();
52410
52411     this.createBody(config);
52412     this.visible = true;
52413     this.collapsed = false;
52414
52415     if(config.hideWhenEmpty){
52416         this.hide();
52417         this.on("paneladded", this.validateVisibility, this);
52418         this.on("panelremoved", this.validateVisibility, this);
52419     }
52420     this.applyConfig(config);
52421 };
52422
52423 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52424
52425     createBody : function(){
52426         /** This region's body element 
52427         * @type Roo.Element */
52428         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52429     },
52430
52431     applyConfig : function(c){
52432         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52433             var dh = Roo.DomHelper;
52434             if(c.titlebar !== false){
52435                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52436                 this.collapseBtn.on("click", this.collapse, this);
52437                 this.collapseBtn.enableDisplayMode();
52438
52439                 if(c.showPin === true || this.showPin){
52440                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52441                     this.stickBtn.enableDisplayMode();
52442                     this.stickBtn.on("click", this.expand, this);
52443                     this.stickBtn.hide();
52444                 }
52445             }
52446             /** This region's collapsed element
52447             * @type Roo.Element */
52448             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52449                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52450             ]}, true);
52451             if(c.floatable !== false){
52452                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52453                this.collapsedEl.on("click", this.collapseClick, this);
52454             }
52455
52456             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52457                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52458                    id: "message", unselectable: "on", style:{"float":"left"}});
52459                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52460              }
52461             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52462             this.expandBtn.on("click", this.expand, this);
52463         }
52464         if(this.collapseBtn){
52465             this.collapseBtn.setVisible(c.collapsible == true);
52466         }
52467         this.cmargins = c.cmargins || this.cmargins ||
52468                          (this.position == "west" || this.position == "east" ?
52469                              {top: 0, left: 2, right:2, bottom: 0} :
52470                              {top: 2, left: 0, right:0, bottom: 2});
52471         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52472         this.bottomTabs = c.tabPosition != "top";
52473         this.autoScroll = c.autoScroll || false;
52474         if(this.autoScroll){
52475             this.bodyEl.setStyle("overflow", "auto");
52476         }else{
52477             this.bodyEl.setStyle("overflow", "hidden");
52478         }
52479         //if(c.titlebar !== false){
52480             if((!c.titlebar && !c.title) || c.titlebar === false){
52481                 this.titleEl.hide();
52482             }else{
52483                 this.titleEl.show();
52484                 if(c.title){
52485                     this.titleTextEl.innerHTML = c.title;
52486                 }
52487             }
52488         //}
52489         this.duration = c.duration || .30;
52490         this.slideDuration = c.slideDuration || .45;
52491         this.config = c;
52492         if(c.collapsed){
52493             this.collapse(true);
52494         }
52495         if(c.hidden){
52496             this.hide();
52497         }
52498     },
52499     /**
52500      * Returns true if this region is currently visible.
52501      * @return {Boolean}
52502      */
52503     isVisible : function(){
52504         return this.visible;
52505     },
52506
52507     /**
52508      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52509      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52510      */
52511     setCollapsedTitle : function(title){
52512         title = title || "&#160;";
52513         if(this.collapsedTitleTextEl){
52514             this.collapsedTitleTextEl.innerHTML = title;
52515         }
52516     },
52517
52518     getBox : function(){
52519         var b;
52520         if(!this.collapsed){
52521             b = this.el.getBox(false, true);
52522         }else{
52523             b = this.collapsedEl.getBox(false, true);
52524         }
52525         return b;
52526     },
52527
52528     getMargins : function(){
52529         return this.collapsed ? this.cmargins : this.margins;
52530     },
52531
52532     highlight : function(){
52533         this.el.addClass("x-layout-panel-dragover");
52534     },
52535
52536     unhighlight : function(){
52537         this.el.removeClass("x-layout-panel-dragover");
52538     },
52539
52540     updateBox : function(box){
52541         this.box = box;
52542         if(!this.collapsed){
52543             this.el.dom.style.left = box.x + "px";
52544             this.el.dom.style.top = box.y + "px";
52545             this.updateBody(box.width, box.height);
52546         }else{
52547             this.collapsedEl.dom.style.left = box.x + "px";
52548             this.collapsedEl.dom.style.top = box.y + "px";
52549             this.collapsedEl.setSize(box.width, box.height);
52550         }
52551         if(this.tabs){
52552             this.tabs.autoSizeTabs();
52553         }
52554     },
52555
52556     updateBody : function(w, h){
52557         if(w !== null){
52558             this.el.setWidth(w);
52559             w -= this.el.getBorderWidth("rl");
52560             if(this.config.adjustments){
52561                 w += this.config.adjustments[0];
52562             }
52563         }
52564         if(h !== null){
52565             this.el.setHeight(h);
52566             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52567             h -= this.el.getBorderWidth("tb");
52568             if(this.config.adjustments){
52569                 h += this.config.adjustments[1];
52570             }
52571             this.bodyEl.setHeight(h);
52572             if(this.tabs){
52573                 h = this.tabs.syncHeight(h);
52574             }
52575         }
52576         if(this.panelSize){
52577             w = w !== null ? w : this.panelSize.width;
52578             h = h !== null ? h : this.panelSize.height;
52579         }
52580         if(this.activePanel){
52581             var el = this.activePanel.getEl();
52582             w = w !== null ? w : el.getWidth();
52583             h = h !== null ? h : el.getHeight();
52584             this.panelSize = {width: w, height: h};
52585             this.activePanel.setSize(w, h);
52586         }
52587         if(Roo.isIE && this.tabs){
52588             this.tabs.el.repaint();
52589         }
52590     },
52591
52592     /**
52593      * Returns the container element for this region.
52594      * @return {Roo.Element}
52595      */
52596     getEl : function(){
52597         return this.el;
52598     },
52599
52600     /**
52601      * Hides this region.
52602      */
52603     hide : function(){
52604         if(!this.collapsed){
52605             this.el.dom.style.left = "-2000px";
52606             this.el.hide();
52607         }else{
52608             this.collapsedEl.dom.style.left = "-2000px";
52609             this.collapsedEl.hide();
52610         }
52611         this.visible = false;
52612         this.fireEvent("visibilitychange", this, false);
52613     },
52614
52615     /**
52616      * Shows this region if it was previously hidden.
52617      */
52618     show : function(){
52619         if(!this.collapsed){
52620             this.el.show();
52621         }else{
52622             this.collapsedEl.show();
52623         }
52624         this.visible = true;
52625         this.fireEvent("visibilitychange", this, true);
52626     },
52627
52628     closeClicked : function(){
52629         if(this.activePanel){
52630             this.remove(this.activePanel);
52631         }
52632     },
52633
52634     collapseClick : function(e){
52635         if(this.isSlid){
52636            e.stopPropagation();
52637            this.slideIn();
52638         }else{
52639            e.stopPropagation();
52640            this.slideOut();
52641         }
52642     },
52643
52644     /**
52645      * Collapses this region.
52646      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52647      */
52648     collapse : function(skipAnim, skipCheck){
52649         if(this.collapsed) {
52650             return;
52651         }
52652         
52653         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52654             
52655             this.collapsed = true;
52656             if(this.split){
52657                 this.split.el.hide();
52658             }
52659             if(this.config.animate && skipAnim !== true){
52660                 this.fireEvent("invalidated", this);
52661                 this.animateCollapse();
52662             }else{
52663                 this.el.setLocation(-20000,-20000);
52664                 this.el.hide();
52665                 this.collapsedEl.show();
52666                 this.fireEvent("collapsed", this);
52667                 this.fireEvent("invalidated", this);
52668             }
52669         }
52670         
52671     },
52672
52673     animateCollapse : function(){
52674         // overridden
52675     },
52676
52677     /**
52678      * Expands this region if it was previously collapsed.
52679      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52680      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52681      */
52682     expand : function(e, skipAnim){
52683         if(e) {
52684             e.stopPropagation();
52685         }
52686         if(!this.collapsed || this.el.hasActiveFx()) {
52687             return;
52688         }
52689         if(this.isSlid){
52690             this.afterSlideIn();
52691             skipAnim = true;
52692         }
52693         this.collapsed = false;
52694         if(this.config.animate && skipAnim !== true){
52695             this.animateExpand();
52696         }else{
52697             this.el.show();
52698             if(this.split){
52699                 this.split.el.show();
52700             }
52701             this.collapsedEl.setLocation(-2000,-2000);
52702             this.collapsedEl.hide();
52703             this.fireEvent("invalidated", this);
52704             this.fireEvent("expanded", this);
52705         }
52706     },
52707
52708     animateExpand : function(){
52709         // overridden
52710     },
52711
52712     initTabs : function()
52713     {
52714         this.bodyEl.setStyle("overflow", "hidden");
52715         var ts = new Roo.TabPanel(
52716                 this.bodyEl.dom,
52717                 {
52718                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52719                     disableTooltips: this.config.disableTabTips,
52720                     toolbar : this.config.toolbar
52721                 }
52722         );
52723         if(this.config.hideTabs){
52724             ts.stripWrap.setDisplayed(false);
52725         }
52726         this.tabs = ts;
52727         ts.resizeTabs = this.config.resizeTabs === true;
52728         ts.minTabWidth = this.config.minTabWidth || 40;
52729         ts.maxTabWidth = this.config.maxTabWidth || 250;
52730         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52731         ts.monitorResize = false;
52732         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52733         ts.bodyEl.addClass('x-layout-tabs-body');
52734         this.panels.each(this.initPanelAsTab, this);
52735     },
52736
52737     initPanelAsTab : function(panel){
52738         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52739                     this.config.closeOnTab && panel.isClosable());
52740         if(panel.tabTip !== undefined){
52741             ti.setTooltip(panel.tabTip);
52742         }
52743         ti.on("activate", function(){
52744               this.setActivePanel(panel);
52745         }, this);
52746         if(this.config.closeOnTab){
52747             ti.on("beforeclose", function(t, e){
52748                 e.cancel = true;
52749                 this.remove(panel);
52750             }, this);
52751         }
52752         return ti;
52753     },
52754
52755     updatePanelTitle : function(panel, title){
52756         if(this.activePanel == panel){
52757             this.updateTitle(title);
52758         }
52759         if(this.tabs){
52760             var ti = this.tabs.getTab(panel.getEl().id);
52761             ti.setText(title);
52762             if(panel.tabTip !== undefined){
52763                 ti.setTooltip(panel.tabTip);
52764             }
52765         }
52766     },
52767
52768     updateTitle : function(title){
52769         if(this.titleTextEl && !this.config.title){
52770             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52771         }
52772     },
52773
52774     setActivePanel : function(panel){
52775         panel = this.getPanel(panel);
52776         if(this.activePanel && this.activePanel != panel){
52777             this.activePanel.setActiveState(false);
52778         }
52779         this.activePanel = panel;
52780         panel.setActiveState(true);
52781         if(this.panelSize){
52782             panel.setSize(this.panelSize.width, this.panelSize.height);
52783         }
52784         if(this.closeBtn){
52785             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52786         }
52787         this.updateTitle(panel.getTitle());
52788         if(this.tabs){
52789             this.fireEvent("invalidated", this);
52790         }
52791         this.fireEvent("panelactivated", this, panel);
52792     },
52793
52794     /**
52795      * Shows the specified panel.
52796      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52797      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52798      */
52799     showPanel : function(panel)
52800     {
52801         panel = this.getPanel(panel);
52802         if(panel){
52803             if(this.tabs){
52804                 var tab = this.tabs.getTab(panel.getEl().id);
52805                 if(tab.isHidden()){
52806                     this.tabs.unhideTab(tab.id);
52807                 }
52808                 tab.activate();
52809             }else{
52810                 this.setActivePanel(panel);
52811             }
52812         }
52813         return panel;
52814     },
52815
52816     /**
52817      * Get the active panel for this region.
52818      * @return {Roo.ContentPanel} The active panel or null
52819      */
52820     getActivePanel : function(){
52821         return this.activePanel;
52822     },
52823
52824     validateVisibility : function(){
52825         if(this.panels.getCount() < 1){
52826             this.updateTitle("&#160;");
52827             this.closeBtn.hide();
52828             this.hide();
52829         }else{
52830             if(!this.isVisible()){
52831                 this.show();
52832             }
52833         }
52834     },
52835
52836     /**
52837      * Adds the passed ContentPanel(s) to this region.
52838      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52839      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52840      */
52841     add : function(panel){
52842         if(arguments.length > 1){
52843             for(var i = 0, len = arguments.length; i < len; i++) {
52844                 this.add(arguments[i]);
52845             }
52846             return null;
52847         }
52848         if(this.hasPanel(panel)){
52849             this.showPanel(panel);
52850             return panel;
52851         }
52852         panel.setRegion(this);
52853         this.panels.add(panel);
52854         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52855             this.bodyEl.dom.appendChild(panel.getEl().dom);
52856             if(panel.background !== true){
52857                 this.setActivePanel(panel);
52858             }
52859             this.fireEvent("paneladded", this, panel);
52860             return panel;
52861         }
52862         if(!this.tabs){
52863             this.initTabs();
52864         }else{
52865             this.initPanelAsTab(panel);
52866         }
52867         if(panel.background !== true){
52868             this.tabs.activate(panel.getEl().id);
52869         }
52870         this.fireEvent("paneladded", this, panel);
52871         return panel;
52872     },
52873
52874     /**
52875      * Hides the tab for the specified panel.
52876      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52877      */
52878     hidePanel : function(panel){
52879         if(this.tabs && (panel = this.getPanel(panel))){
52880             this.tabs.hideTab(panel.getEl().id);
52881         }
52882     },
52883
52884     /**
52885      * Unhides the tab for a previously hidden panel.
52886      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52887      */
52888     unhidePanel : function(panel){
52889         if(this.tabs && (panel = this.getPanel(panel))){
52890             this.tabs.unhideTab(panel.getEl().id);
52891         }
52892     },
52893
52894     clearPanels : function(){
52895         while(this.panels.getCount() > 0){
52896              this.remove(this.panels.first());
52897         }
52898     },
52899
52900     /**
52901      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52902      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52903      * @param {Boolean} preservePanel Overrides the config preservePanel option
52904      * @return {Roo.ContentPanel} The panel that was removed
52905      */
52906     remove : function(panel, preservePanel){
52907         panel = this.getPanel(panel);
52908         if(!panel){
52909             return null;
52910         }
52911         var e = {};
52912         this.fireEvent("beforeremove", this, panel, e);
52913         if(e.cancel === true){
52914             return null;
52915         }
52916         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52917         var panelId = panel.getId();
52918         this.panels.removeKey(panelId);
52919         if(preservePanel){
52920             document.body.appendChild(panel.getEl().dom);
52921         }
52922         if(this.tabs){
52923             this.tabs.removeTab(panel.getEl().id);
52924         }else if (!preservePanel){
52925             this.bodyEl.dom.removeChild(panel.getEl().dom);
52926         }
52927         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52928             var p = this.panels.first();
52929             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52930             tempEl.appendChild(p.getEl().dom);
52931             this.bodyEl.update("");
52932             this.bodyEl.dom.appendChild(p.getEl().dom);
52933             tempEl = null;
52934             this.updateTitle(p.getTitle());
52935             this.tabs = null;
52936             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52937             this.setActivePanel(p);
52938         }
52939         panel.setRegion(null);
52940         if(this.activePanel == panel){
52941             this.activePanel = null;
52942         }
52943         if(this.config.autoDestroy !== false && preservePanel !== true){
52944             try{panel.destroy();}catch(e){}
52945         }
52946         this.fireEvent("panelremoved", this, panel);
52947         return panel;
52948     },
52949
52950     /**
52951      * Returns the TabPanel component used by this region
52952      * @return {Roo.TabPanel}
52953      */
52954     getTabs : function(){
52955         return this.tabs;
52956     },
52957
52958     createTool : function(parentEl, className){
52959         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52960             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52961         btn.addClassOnOver("x-layout-tools-button-over");
52962         return btn;
52963     }
52964 });/*
52965  * Based on:
52966  * Ext JS Library 1.1.1
52967  * Copyright(c) 2006-2007, Ext JS, LLC.
52968  *
52969  * Originally Released Under LGPL - original licence link has changed is not relivant.
52970  *
52971  * Fork - LGPL
52972  * <script type="text/javascript">
52973  */
52974  
52975
52976
52977 /**
52978  * @class Roo.SplitLayoutRegion
52979  * @extends Roo.LayoutRegion
52980  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52981  */
52982 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52983     this.cursor = cursor;
52984     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52985 };
52986
52987 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52988     splitTip : "Drag to resize.",
52989     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52990     useSplitTips : false,
52991
52992     applyConfig : function(config){
52993         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52994         if(config.split){
52995             if(!this.split){
52996                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52997                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52998                 /** The SplitBar for this region 
52999                 * @type Roo.SplitBar */
53000                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53001                 this.split.on("moved", this.onSplitMove, this);
53002                 this.split.useShim = config.useShim === true;
53003                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53004                 if(this.useSplitTips){
53005                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53006                 }
53007                 if(config.collapsible){
53008                     this.split.el.on("dblclick", this.collapse,  this);
53009                 }
53010             }
53011             if(typeof config.minSize != "undefined"){
53012                 this.split.minSize = config.minSize;
53013             }
53014             if(typeof config.maxSize != "undefined"){
53015                 this.split.maxSize = config.maxSize;
53016             }
53017             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53018                 this.hideSplitter();
53019             }
53020         }
53021     },
53022
53023     getHMaxSize : function(){
53024          var cmax = this.config.maxSize || 10000;
53025          var center = this.mgr.getRegion("center");
53026          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53027     },
53028
53029     getVMaxSize : function(){
53030          var cmax = this.config.maxSize || 10000;
53031          var center = this.mgr.getRegion("center");
53032          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53033     },
53034
53035     onSplitMove : function(split, newSize){
53036         this.fireEvent("resized", this, newSize);
53037     },
53038     
53039     /** 
53040      * Returns the {@link Roo.SplitBar} for this region.
53041      * @return {Roo.SplitBar}
53042      */
53043     getSplitBar : function(){
53044         return this.split;
53045     },
53046     
53047     hide : function(){
53048         this.hideSplitter();
53049         Roo.SplitLayoutRegion.superclass.hide.call(this);
53050     },
53051
53052     hideSplitter : function(){
53053         if(this.split){
53054             this.split.el.setLocation(-2000,-2000);
53055             this.split.el.hide();
53056         }
53057     },
53058
53059     show : function(){
53060         if(this.split){
53061             this.split.el.show();
53062         }
53063         Roo.SplitLayoutRegion.superclass.show.call(this);
53064     },
53065     
53066     beforeSlide: function(){
53067         if(Roo.isGecko){// firefox overflow auto bug workaround
53068             this.bodyEl.clip();
53069             if(this.tabs) {
53070                 this.tabs.bodyEl.clip();
53071             }
53072             if(this.activePanel){
53073                 this.activePanel.getEl().clip();
53074                 
53075                 if(this.activePanel.beforeSlide){
53076                     this.activePanel.beforeSlide();
53077                 }
53078             }
53079         }
53080     },
53081     
53082     afterSlide : function(){
53083         if(Roo.isGecko){// firefox overflow auto bug workaround
53084             this.bodyEl.unclip();
53085             if(this.tabs) {
53086                 this.tabs.bodyEl.unclip();
53087             }
53088             if(this.activePanel){
53089                 this.activePanel.getEl().unclip();
53090                 if(this.activePanel.afterSlide){
53091                     this.activePanel.afterSlide();
53092                 }
53093             }
53094         }
53095     },
53096
53097     initAutoHide : function(){
53098         if(this.autoHide !== false){
53099             if(!this.autoHideHd){
53100                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53101                 this.autoHideHd = {
53102                     "mouseout": function(e){
53103                         if(!e.within(this.el, true)){
53104                             st.delay(500);
53105                         }
53106                     },
53107                     "mouseover" : function(e){
53108                         st.cancel();
53109                     },
53110                     scope : this
53111                 };
53112             }
53113             this.el.on(this.autoHideHd);
53114         }
53115     },
53116
53117     clearAutoHide : function(){
53118         if(this.autoHide !== false){
53119             this.el.un("mouseout", this.autoHideHd.mouseout);
53120             this.el.un("mouseover", this.autoHideHd.mouseover);
53121         }
53122     },
53123
53124     clearMonitor : function(){
53125         Roo.get(document).un("click", this.slideInIf, this);
53126     },
53127
53128     // these names are backwards but not changed for compat
53129     slideOut : function(){
53130         if(this.isSlid || this.el.hasActiveFx()){
53131             return;
53132         }
53133         this.isSlid = true;
53134         if(this.collapseBtn){
53135             this.collapseBtn.hide();
53136         }
53137         this.closeBtnState = this.closeBtn.getStyle('display');
53138         this.closeBtn.hide();
53139         if(this.stickBtn){
53140             this.stickBtn.show();
53141         }
53142         this.el.show();
53143         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53144         this.beforeSlide();
53145         this.el.setStyle("z-index", 10001);
53146         this.el.slideIn(this.getSlideAnchor(), {
53147             callback: function(){
53148                 this.afterSlide();
53149                 this.initAutoHide();
53150                 Roo.get(document).on("click", this.slideInIf, this);
53151                 this.fireEvent("slideshow", this);
53152             },
53153             scope: this,
53154             block: true
53155         });
53156     },
53157
53158     afterSlideIn : function(){
53159         this.clearAutoHide();
53160         this.isSlid = false;
53161         this.clearMonitor();
53162         this.el.setStyle("z-index", "");
53163         if(this.collapseBtn){
53164             this.collapseBtn.show();
53165         }
53166         this.closeBtn.setStyle('display', this.closeBtnState);
53167         if(this.stickBtn){
53168             this.stickBtn.hide();
53169         }
53170         this.fireEvent("slidehide", this);
53171     },
53172
53173     slideIn : function(cb){
53174         if(!this.isSlid || this.el.hasActiveFx()){
53175             Roo.callback(cb);
53176             return;
53177         }
53178         this.isSlid = false;
53179         this.beforeSlide();
53180         this.el.slideOut(this.getSlideAnchor(), {
53181             callback: function(){
53182                 this.el.setLeftTop(-10000, -10000);
53183                 this.afterSlide();
53184                 this.afterSlideIn();
53185                 Roo.callback(cb);
53186             },
53187             scope: this,
53188             block: true
53189         });
53190     },
53191     
53192     slideInIf : function(e){
53193         if(!e.within(this.el)){
53194             this.slideIn();
53195         }
53196     },
53197
53198     animateCollapse : function(){
53199         this.beforeSlide();
53200         this.el.setStyle("z-index", 20000);
53201         var anchor = this.getSlideAnchor();
53202         this.el.slideOut(anchor, {
53203             callback : function(){
53204                 this.el.setStyle("z-index", "");
53205                 this.collapsedEl.slideIn(anchor, {duration:.3});
53206                 this.afterSlide();
53207                 this.el.setLocation(-10000,-10000);
53208                 this.el.hide();
53209                 this.fireEvent("collapsed", this);
53210             },
53211             scope: this,
53212             block: true
53213         });
53214     },
53215
53216     animateExpand : function(){
53217         this.beforeSlide();
53218         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53219         this.el.setStyle("z-index", 20000);
53220         this.collapsedEl.hide({
53221             duration:.1
53222         });
53223         this.el.slideIn(this.getSlideAnchor(), {
53224             callback : function(){
53225                 this.el.setStyle("z-index", "");
53226                 this.afterSlide();
53227                 if(this.split){
53228                     this.split.el.show();
53229                 }
53230                 this.fireEvent("invalidated", this);
53231                 this.fireEvent("expanded", this);
53232             },
53233             scope: this,
53234             block: true
53235         });
53236     },
53237
53238     anchors : {
53239         "west" : "left",
53240         "east" : "right",
53241         "north" : "top",
53242         "south" : "bottom"
53243     },
53244
53245     sanchors : {
53246         "west" : "l",
53247         "east" : "r",
53248         "north" : "t",
53249         "south" : "b"
53250     },
53251
53252     canchors : {
53253         "west" : "tl-tr",
53254         "east" : "tr-tl",
53255         "north" : "tl-bl",
53256         "south" : "bl-tl"
53257     },
53258
53259     getAnchor : function(){
53260         return this.anchors[this.position];
53261     },
53262
53263     getCollapseAnchor : function(){
53264         return this.canchors[this.position];
53265     },
53266
53267     getSlideAnchor : function(){
53268         return this.sanchors[this.position];
53269     },
53270
53271     getAlignAdj : function(){
53272         var cm = this.cmargins;
53273         switch(this.position){
53274             case "west":
53275                 return [0, 0];
53276             break;
53277             case "east":
53278                 return [0, 0];
53279             break;
53280             case "north":
53281                 return [0, 0];
53282             break;
53283             case "south":
53284                 return [0, 0];
53285             break;
53286         }
53287     },
53288
53289     getExpandAdj : function(){
53290         var c = this.collapsedEl, cm = this.cmargins;
53291         switch(this.position){
53292             case "west":
53293                 return [-(cm.right+c.getWidth()+cm.left), 0];
53294             break;
53295             case "east":
53296                 return [cm.right+c.getWidth()+cm.left, 0];
53297             break;
53298             case "north":
53299                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53300             break;
53301             case "south":
53302                 return [0, cm.top+cm.bottom+c.getHeight()];
53303             break;
53304         }
53305     }
53306 });/*
53307  * Based on:
53308  * Ext JS Library 1.1.1
53309  * Copyright(c) 2006-2007, Ext JS, LLC.
53310  *
53311  * Originally Released Under LGPL - original licence link has changed is not relivant.
53312  *
53313  * Fork - LGPL
53314  * <script type="text/javascript">
53315  */
53316 /*
53317  * These classes are private internal classes
53318  */
53319 Roo.CenterLayoutRegion = function(mgr, config){
53320     Roo.LayoutRegion.call(this, mgr, config, "center");
53321     this.visible = true;
53322     this.minWidth = config.minWidth || 20;
53323     this.minHeight = config.minHeight || 20;
53324 };
53325
53326 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53327     hide : function(){
53328         // center panel can't be hidden
53329     },
53330     
53331     show : function(){
53332         // center panel can't be hidden
53333     },
53334     
53335     getMinWidth: function(){
53336         return this.minWidth;
53337     },
53338     
53339     getMinHeight: function(){
53340         return this.minHeight;
53341     }
53342 });
53343
53344
53345 Roo.NorthLayoutRegion = function(mgr, config){
53346     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53347     if(this.split){
53348         this.split.placement = Roo.SplitBar.TOP;
53349         this.split.orientation = Roo.SplitBar.VERTICAL;
53350         this.split.el.addClass("x-layout-split-v");
53351     }
53352     var size = config.initialSize || config.height;
53353     if(typeof size != "undefined"){
53354         this.el.setHeight(size);
53355     }
53356 };
53357 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53358     orientation: Roo.SplitBar.VERTICAL,
53359     getBox : function(){
53360         if(this.collapsed){
53361             return this.collapsedEl.getBox();
53362         }
53363         var box = this.el.getBox();
53364         if(this.split){
53365             box.height += this.split.el.getHeight();
53366         }
53367         return box;
53368     },
53369     
53370     updateBox : function(box){
53371         if(this.split && !this.collapsed){
53372             box.height -= this.split.el.getHeight();
53373             this.split.el.setLeft(box.x);
53374             this.split.el.setTop(box.y+box.height);
53375             this.split.el.setWidth(box.width);
53376         }
53377         if(this.collapsed){
53378             this.updateBody(box.width, null);
53379         }
53380         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53381     }
53382 });
53383
53384 Roo.SouthLayoutRegion = function(mgr, config){
53385     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53386     if(this.split){
53387         this.split.placement = Roo.SplitBar.BOTTOM;
53388         this.split.orientation = Roo.SplitBar.VERTICAL;
53389         this.split.el.addClass("x-layout-split-v");
53390     }
53391     var size = config.initialSize || config.height;
53392     if(typeof size != "undefined"){
53393         this.el.setHeight(size);
53394     }
53395 };
53396 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53397     orientation: Roo.SplitBar.VERTICAL,
53398     getBox : function(){
53399         if(this.collapsed){
53400             return this.collapsedEl.getBox();
53401         }
53402         var box = this.el.getBox();
53403         if(this.split){
53404             var sh = this.split.el.getHeight();
53405             box.height += sh;
53406             box.y -= sh;
53407         }
53408         return box;
53409     },
53410     
53411     updateBox : function(box){
53412         if(this.split && !this.collapsed){
53413             var sh = this.split.el.getHeight();
53414             box.height -= sh;
53415             box.y += sh;
53416             this.split.el.setLeft(box.x);
53417             this.split.el.setTop(box.y-sh);
53418             this.split.el.setWidth(box.width);
53419         }
53420         if(this.collapsed){
53421             this.updateBody(box.width, null);
53422         }
53423         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53424     }
53425 });
53426
53427 Roo.EastLayoutRegion = function(mgr, config){
53428     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53429     if(this.split){
53430         this.split.placement = Roo.SplitBar.RIGHT;
53431         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53432         this.split.el.addClass("x-layout-split-h");
53433     }
53434     var size = config.initialSize || config.width;
53435     if(typeof size != "undefined"){
53436         this.el.setWidth(size);
53437     }
53438 };
53439 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53440     orientation: Roo.SplitBar.HORIZONTAL,
53441     getBox : function(){
53442         if(this.collapsed){
53443             return this.collapsedEl.getBox();
53444         }
53445         var box = this.el.getBox();
53446         if(this.split){
53447             var sw = this.split.el.getWidth();
53448             box.width += sw;
53449             box.x -= sw;
53450         }
53451         return box;
53452     },
53453
53454     updateBox : function(box){
53455         if(this.split && !this.collapsed){
53456             var sw = this.split.el.getWidth();
53457             box.width -= sw;
53458             this.split.el.setLeft(box.x);
53459             this.split.el.setTop(box.y);
53460             this.split.el.setHeight(box.height);
53461             box.x += sw;
53462         }
53463         if(this.collapsed){
53464             this.updateBody(null, box.height);
53465         }
53466         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53467     }
53468 });
53469
53470 Roo.WestLayoutRegion = function(mgr, config){
53471     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53472     if(this.split){
53473         this.split.placement = Roo.SplitBar.LEFT;
53474         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53475         this.split.el.addClass("x-layout-split-h");
53476     }
53477     var size = config.initialSize || config.width;
53478     if(typeof size != "undefined"){
53479         this.el.setWidth(size);
53480     }
53481 };
53482 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53483     orientation: Roo.SplitBar.HORIZONTAL,
53484     getBox : function(){
53485         if(this.collapsed){
53486             return this.collapsedEl.getBox();
53487         }
53488         var box = this.el.getBox();
53489         if(this.split){
53490             box.width += this.split.el.getWidth();
53491         }
53492         return box;
53493     },
53494     
53495     updateBox : function(box){
53496         if(this.split && !this.collapsed){
53497             var sw = this.split.el.getWidth();
53498             box.width -= sw;
53499             this.split.el.setLeft(box.x+box.width);
53500             this.split.el.setTop(box.y);
53501             this.split.el.setHeight(box.height);
53502         }
53503         if(this.collapsed){
53504             this.updateBody(null, box.height);
53505         }
53506         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53507     }
53508 });
53509 /*
53510  * Based on:
53511  * Ext JS Library 1.1.1
53512  * Copyright(c) 2006-2007, Ext JS, LLC.
53513  *
53514  * Originally Released Under LGPL - original licence link has changed is not relivant.
53515  *
53516  * Fork - LGPL
53517  * <script type="text/javascript">
53518  */
53519  
53520  
53521 /*
53522  * Private internal class for reading and applying state
53523  */
53524 Roo.LayoutStateManager = function(layout){
53525      // default empty state
53526      this.state = {
53527         north: {},
53528         south: {},
53529         east: {},
53530         west: {}       
53531     };
53532 };
53533
53534 Roo.LayoutStateManager.prototype = {
53535     init : function(layout, provider){
53536         this.provider = provider;
53537         var state = provider.get(layout.id+"-layout-state");
53538         if(state){
53539             var wasUpdating = layout.isUpdating();
53540             if(!wasUpdating){
53541                 layout.beginUpdate();
53542             }
53543             for(var key in state){
53544                 if(typeof state[key] != "function"){
53545                     var rstate = state[key];
53546                     var r = layout.getRegion(key);
53547                     if(r && rstate){
53548                         if(rstate.size){
53549                             r.resizeTo(rstate.size);
53550                         }
53551                         if(rstate.collapsed == true){
53552                             r.collapse(true);
53553                         }else{
53554                             r.expand(null, true);
53555                         }
53556                     }
53557                 }
53558             }
53559             if(!wasUpdating){
53560                 layout.endUpdate();
53561             }
53562             this.state = state; 
53563         }
53564         this.layout = layout;
53565         layout.on("regionresized", this.onRegionResized, this);
53566         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53567         layout.on("regionexpanded", this.onRegionExpanded, this);
53568     },
53569     
53570     storeState : function(){
53571         this.provider.set(this.layout.id+"-layout-state", this.state);
53572     },
53573     
53574     onRegionResized : function(region, newSize){
53575         this.state[region.getPosition()].size = newSize;
53576         this.storeState();
53577     },
53578     
53579     onRegionCollapsed : function(region){
53580         this.state[region.getPosition()].collapsed = true;
53581         this.storeState();
53582     },
53583     
53584     onRegionExpanded : function(region){
53585         this.state[region.getPosition()].collapsed = false;
53586         this.storeState();
53587     }
53588 };/*
53589  * Based on:
53590  * Ext JS Library 1.1.1
53591  * Copyright(c) 2006-2007, Ext JS, LLC.
53592  *
53593  * Originally Released Under LGPL - original licence link has changed is not relivant.
53594  *
53595  * Fork - LGPL
53596  * <script type="text/javascript">
53597  */
53598 /**
53599  * @class Roo.ContentPanel
53600  * @extends Roo.util.Observable
53601  * A basic ContentPanel element.
53602  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53603  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53604  * @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
53605  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53606  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53607  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53608  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53609  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53610  * @cfg {String} title          The title for this panel
53611  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53612  * @cfg {String} url            Calls {@link #setUrl} with this value
53613  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53614  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53615  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53616  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53617
53618  * @constructor
53619  * Create a new ContentPanel.
53620  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53621  * @param {String/Object} config A string to set only the title or a config object
53622  * @param {String} content (optional) Set the HTML content for this panel
53623  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53624  */
53625 Roo.ContentPanel = function(el, config, content){
53626     
53627      
53628     /*
53629     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53630         config = el;
53631         el = Roo.id();
53632     }
53633     if (config && config.parentLayout) { 
53634         el = config.parentLayout.el.createChild(); 
53635     }
53636     */
53637     if(el.autoCreate){ // xtype is available if this is called from factory
53638         config = el;
53639         el = Roo.id();
53640     }
53641     this.el = Roo.get(el);
53642     if(!this.el && config && config.autoCreate){
53643         if(typeof config.autoCreate == "object"){
53644             if(!config.autoCreate.id){
53645                 config.autoCreate.id = config.id||el;
53646             }
53647             this.el = Roo.DomHelper.append(document.body,
53648                         config.autoCreate, true);
53649         }else{
53650             this.el = Roo.DomHelper.append(document.body,
53651                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53652         }
53653     }
53654     this.closable = false;
53655     this.loaded = false;
53656     this.active = false;
53657     if(typeof config == "string"){
53658         this.title = config;
53659     }else{
53660         Roo.apply(this, config);
53661     }
53662     
53663     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53664         this.wrapEl = this.el.wrap();
53665         this.toolbar.container = this.el.insertSibling(false, 'before');
53666         this.toolbar = new Roo.Toolbar(this.toolbar);
53667     }
53668     
53669     // xtype created footer. - not sure if will work as we normally have to render first..
53670     if (this.footer && !this.footer.el && this.footer.xtype) {
53671         if (!this.wrapEl) {
53672             this.wrapEl = this.el.wrap();
53673         }
53674     
53675         this.footer.container = this.wrapEl.createChild();
53676          
53677         this.footer = Roo.factory(this.footer, Roo);
53678         
53679     }
53680     
53681     if(this.resizeEl){
53682         this.resizeEl = Roo.get(this.resizeEl, true);
53683     }else{
53684         this.resizeEl = this.el;
53685     }
53686     // handle view.xtype
53687     
53688  
53689     
53690     
53691     this.addEvents({
53692         /**
53693          * @event activate
53694          * Fires when this panel is activated. 
53695          * @param {Roo.ContentPanel} this
53696          */
53697         "activate" : true,
53698         /**
53699          * @event deactivate
53700          * Fires when this panel is activated. 
53701          * @param {Roo.ContentPanel} this
53702          */
53703         "deactivate" : true,
53704
53705         /**
53706          * @event resize
53707          * Fires when this panel is resized if fitToFrame is true.
53708          * @param {Roo.ContentPanel} this
53709          * @param {Number} width The width after any component adjustments
53710          * @param {Number} height The height after any component adjustments
53711          */
53712         "resize" : true,
53713         
53714          /**
53715          * @event render
53716          * Fires when this tab is created
53717          * @param {Roo.ContentPanel} this
53718          */
53719         "render" : true
53720          
53721         
53722     });
53723     
53724
53725     
53726     
53727     if(this.autoScroll){
53728         this.resizeEl.setStyle("overflow", "auto");
53729     } else {
53730         // fix randome scrolling
53731         this.el.on('scroll', function() {
53732             Roo.log('fix random scolling');
53733             this.scrollTo('top',0); 
53734         });
53735     }
53736     content = content || this.content;
53737     if(content){
53738         this.setContent(content);
53739     }
53740     if(config && config.url){
53741         this.setUrl(this.url, this.params, this.loadOnce);
53742     }
53743     
53744     
53745     
53746     Roo.ContentPanel.superclass.constructor.call(this);
53747     
53748     if (this.view && typeof(this.view.xtype) != 'undefined') {
53749         this.view.el = this.el.appendChild(document.createElement("div"));
53750         this.view = Roo.factory(this.view); 
53751         this.view.render  &&  this.view.render(false, '');  
53752     }
53753     
53754     
53755     this.fireEvent('render', this);
53756 };
53757
53758 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53759     tabTip:'',
53760     setRegion : function(region){
53761         this.region = region;
53762         if(region){
53763            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53764         }else{
53765            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53766         } 
53767     },
53768     
53769     /**
53770      * Returns the toolbar for this Panel if one was configured. 
53771      * @return {Roo.Toolbar} 
53772      */
53773     getToolbar : function(){
53774         return this.toolbar;
53775     },
53776     
53777     setActiveState : function(active){
53778         this.active = active;
53779         if(!active){
53780             this.fireEvent("deactivate", this);
53781         }else{
53782             this.fireEvent("activate", this);
53783         }
53784     },
53785     /**
53786      * Updates this panel's element
53787      * @param {String} content The new content
53788      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53789     */
53790     setContent : function(content, loadScripts){
53791         this.el.update(content, loadScripts);
53792     },
53793
53794     ignoreResize : function(w, h){
53795         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53796             return true;
53797         }else{
53798             this.lastSize = {width: w, height: h};
53799             return false;
53800         }
53801     },
53802     /**
53803      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53804      * @return {Roo.UpdateManager} The UpdateManager
53805      */
53806     getUpdateManager : function(){
53807         return this.el.getUpdateManager();
53808     },
53809      /**
53810      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53811      * @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:
53812 <pre><code>
53813 panel.load({
53814     url: "your-url.php",
53815     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53816     callback: yourFunction,
53817     scope: yourObject, //(optional scope)
53818     discardUrl: false,
53819     nocache: false,
53820     text: "Loading...",
53821     timeout: 30,
53822     scripts: false
53823 });
53824 </code></pre>
53825      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53826      * 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.
53827      * @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}
53828      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53829      * @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.
53830      * @return {Roo.ContentPanel} this
53831      */
53832     load : function(){
53833         var um = this.el.getUpdateManager();
53834         um.update.apply(um, arguments);
53835         return this;
53836     },
53837
53838
53839     /**
53840      * 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.
53841      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53842      * @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)
53843      * @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)
53844      * @return {Roo.UpdateManager} The UpdateManager
53845      */
53846     setUrl : function(url, params, loadOnce){
53847         if(this.refreshDelegate){
53848             this.removeListener("activate", this.refreshDelegate);
53849         }
53850         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53851         this.on("activate", this.refreshDelegate);
53852         return this.el.getUpdateManager();
53853     },
53854     
53855     _handleRefresh : function(url, params, loadOnce){
53856         if(!loadOnce || !this.loaded){
53857             var updater = this.el.getUpdateManager();
53858             updater.update(url, params, this._setLoaded.createDelegate(this));
53859         }
53860     },
53861     
53862     _setLoaded : function(){
53863         this.loaded = true;
53864     }, 
53865     
53866     /**
53867      * Returns this panel's id
53868      * @return {String} 
53869      */
53870     getId : function(){
53871         return this.el.id;
53872     },
53873     
53874     /** 
53875      * Returns this panel's element - used by regiosn to add.
53876      * @return {Roo.Element} 
53877      */
53878     getEl : function(){
53879         return this.wrapEl || this.el;
53880     },
53881     
53882     adjustForComponents : function(width, height)
53883     {
53884         //Roo.log('adjustForComponents ');
53885         if(this.resizeEl != this.el){
53886             width -= this.el.getFrameWidth('lr');
53887             height -= this.el.getFrameWidth('tb');
53888         }
53889         if(this.toolbar){
53890             var te = this.toolbar.getEl();
53891             height -= te.getHeight();
53892             te.setWidth(width);
53893         }
53894         if(this.footer){
53895             var te = this.footer.getEl();
53896             //Roo.log("footer:" + te.getHeight());
53897             
53898             height -= te.getHeight();
53899             te.setWidth(width);
53900         }
53901         
53902         
53903         if(this.adjustments){
53904             width += this.adjustments[0];
53905             height += this.adjustments[1];
53906         }
53907         return {"width": width, "height": height};
53908     },
53909     
53910     setSize : function(width, height){
53911         if(this.fitToFrame && !this.ignoreResize(width, height)){
53912             if(this.fitContainer && this.resizeEl != this.el){
53913                 this.el.setSize(width, height);
53914             }
53915             var size = this.adjustForComponents(width, height);
53916             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53917             this.fireEvent('resize', this, size.width, size.height);
53918         }
53919     },
53920     
53921     /**
53922      * Returns this panel's title
53923      * @return {String} 
53924      */
53925     getTitle : function(){
53926         return this.title;
53927     },
53928     
53929     /**
53930      * Set this panel's title
53931      * @param {String} title
53932      */
53933     setTitle : function(title){
53934         this.title = title;
53935         if(this.region){
53936             this.region.updatePanelTitle(this, title);
53937         }
53938     },
53939     
53940     /**
53941      * Returns true is this panel was configured to be closable
53942      * @return {Boolean} 
53943      */
53944     isClosable : function(){
53945         return this.closable;
53946     },
53947     
53948     beforeSlide : function(){
53949         this.el.clip();
53950         this.resizeEl.clip();
53951     },
53952     
53953     afterSlide : function(){
53954         this.el.unclip();
53955         this.resizeEl.unclip();
53956     },
53957     
53958     /**
53959      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53960      *   Will fail silently if the {@link #setUrl} method has not been called.
53961      *   This does not activate the panel, just updates its content.
53962      */
53963     refresh : function(){
53964         if(this.refreshDelegate){
53965            this.loaded = false;
53966            this.refreshDelegate();
53967         }
53968     },
53969     
53970     /**
53971      * Destroys this panel
53972      */
53973     destroy : function(){
53974         this.el.removeAllListeners();
53975         var tempEl = document.createElement("span");
53976         tempEl.appendChild(this.el.dom);
53977         tempEl.innerHTML = "";
53978         this.el.remove();
53979         this.el = null;
53980     },
53981     
53982     /**
53983      * form - if the content panel contains a form - this is a reference to it.
53984      * @type {Roo.form.Form}
53985      */
53986     form : false,
53987     /**
53988      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53989      *    This contains a reference to it.
53990      * @type {Roo.View}
53991      */
53992     view : false,
53993     
53994       /**
53995      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53996      * <pre><code>
53997
53998 layout.addxtype({
53999        xtype : 'Form',
54000        items: [ .... ]
54001    }
54002 );
54003
54004 </code></pre>
54005      * @param {Object} cfg Xtype definition of item to add.
54006      */
54007     
54008     addxtype : function(cfg) {
54009         // add form..
54010         if (cfg.xtype.match(/^Form$/)) {
54011             
54012             var el;
54013             //if (this.footer) {
54014             //    el = this.footer.container.insertSibling(false, 'before');
54015             //} else {
54016                 el = this.el.createChild();
54017             //}
54018
54019             this.form = new  Roo.form.Form(cfg);
54020             
54021             
54022             if ( this.form.allItems.length) {
54023                 this.form.render(el.dom);
54024             }
54025             return this.form;
54026         }
54027         // should only have one of theses..
54028         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54029             // views.. should not be just added - used named prop 'view''
54030             
54031             cfg.el = this.el.appendChild(document.createElement("div"));
54032             // factory?
54033             
54034             var ret = new Roo.factory(cfg);
54035              
54036              ret.render && ret.render(false, ''); // render blank..
54037             this.view = ret;
54038             return ret;
54039         }
54040         return false;
54041     }
54042 });
54043
54044 /**
54045  * @class Roo.GridPanel
54046  * @extends Roo.ContentPanel
54047  * @constructor
54048  * Create a new GridPanel.
54049  * @param {Roo.grid.Grid} grid The grid for this panel
54050  * @param {String/Object} config A string to set only the panel's title, or a config object
54051  */
54052 Roo.GridPanel = function(grid, config){
54053     
54054   
54055     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54056         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54057         
54058     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54059     
54060     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54061     
54062     if(this.toolbar){
54063         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54064     }
54065     // xtype created footer. - not sure if will work as we normally have to render first..
54066     if (this.footer && !this.footer.el && this.footer.xtype) {
54067         
54068         this.footer.container = this.grid.getView().getFooterPanel(true);
54069         this.footer.dataSource = this.grid.dataSource;
54070         this.footer = Roo.factory(this.footer, Roo);
54071         
54072     }
54073     
54074     grid.monitorWindowResize = false; // turn off autosizing
54075     grid.autoHeight = false;
54076     grid.autoWidth = false;
54077     this.grid = grid;
54078     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54079 };
54080
54081 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54082     getId : function(){
54083         return this.grid.id;
54084     },
54085     
54086     /**
54087      * Returns the grid for this panel
54088      * @return {Roo.grid.Grid} 
54089      */
54090     getGrid : function(){
54091         return this.grid;    
54092     },
54093     
54094     setSize : function(width, height){
54095         if(!this.ignoreResize(width, height)){
54096             var grid = this.grid;
54097             var size = this.adjustForComponents(width, height);
54098             grid.getGridEl().setSize(size.width, size.height);
54099             grid.autoSize();
54100         }
54101     },
54102     
54103     beforeSlide : function(){
54104         this.grid.getView().scroller.clip();
54105     },
54106     
54107     afterSlide : function(){
54108         this.grid.getView().scroller.unclip();
54109     },
54110     
54111     destroy : function(){
54112         this.grid.destroy();
54113         delete this.grid;
54114         Roo.GridPanel.superclass.destroy.call(this); 
54115     }
54116 });
54117
54118
54119 /**
54120  * @class Roo.NestedLayoutPanel
54121  * @extends Roo.ContentPanel
54122  * @constructor
54123  * Create a new NestedLayoutPanel.
54124  * 
54125  * 
54126  * @param {Roo.BorderLayout} layout The layout for this panel
54127  * @param {String/Object} config A string to set only the title or a config object
54128  */
54129 Roo.NestedLayoutPanel = function(layout, config)
54130 {
54131     // construct with only one argument..
54132     /* FIXME - implement nicer consturctors
54133     if (layout.layout) {
54134         config = layout;
54135         layout = config.layout;
54136         delete config.layout;
54137     }
54138     if (layout.xtype && !layout.getEl) {
54139         // then layout needs constructing..
54140         layout = Roo.factory(layout, Roo);
54141     }
54142     */
54143     
54144     
54145     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54146     
54147     layout.monitorWindowResize = false; // turn off autosizing
54148     this.layout = layout;
54149     this.layout.getEl().addClass("x-layout-nested-layout");
54150     
54151     
54152     
54153     
54154 };
54155
54156 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54157
54158     setSize : function(width, height){
54159         if(!this.ignoreResize(width, height)){
54160             var size = this.adjustForComponents(width, height);
54161             var el = this.layout.getEl();
54162             el.setSize(size.width, size.height);
54163             var touch = el.dom.offsetWidth;
54164             this.layout.layout();
54165             // ie requires a double layout on the first pass
54166             if(Roo.isIE && !this.initialized){
54167                 this.initialized = true;
54168                 this.layout.layout();
54169             }
54170         }
54171     },
54172     
54173     // activate all subpanels if not currently active..
54174     
54175     setActiveState : function(active){
54176         this.active = active;
54177         if(!active){
54178             this.fireEvent("deactivate", this);
54179             return;
54180         }
54181         
54182         this.fireEvent("activate", this);
54183         // not sure if this should happen before or after..
54184         if (!this.layout) {
54185             return; // should not happen..
54186         }
54187         var reg = false;
54188         for (var r in this.layout.regions) {
54189             reg = this.layout.getRegion(r);
54190             if (reg.getActivePanel()) {
54191                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54192                 reg.setActivePanel(reg.getActivePanel());
54193                 continue;
54194             }
54195             if (!reg.panels.length) {
54196                 continue;
54197             }
54198             reg.showPanel(reg.getPanel(0));
54199         }
54200         
54201         
54202         
54203         
54204     },
54205     
54206     /**
54207      * Returns the nested BorderLayout for this panel
54208      * @return {Roo.BorderLayout} 
54209      */
54210     getLayout : function(){
54211         return this.layout;
54212     },
54213     
54214      /**
54215      * Adds a xtype elements to the layout of the nested panel
54216      * <pre><code>
54217
54218 panel.addxtype({
54219        xtype : 'ContentPanel',
54220        region: 'west',
54221        items: [ .... ]
54222    }
54223 );
54224
54225 panel.addxtype({
54226         xtype : 'NestedLayoutPanel',
54227         region: 'west',
54228         layout: {
54229            center: { },
54230            west: { }   
54231         },
54232         items : [ ... list of content panels or nested layout panels.. ]
54233    }
54234 );
54235 </code></pre>
54236      * @param {Object} cfg Xtype definition of item to add.
54237      */
54238     addxtype : function(cfg) {
54239         return this.layout.addxtype(cfg);
54240     
54241     }
54242 });
54243
54244 Roo.ScrollPanel = function(el, config, content){
54245     config = config || {};
54246     config.fitToFrame = true;
54247     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54248     
54249     this.el.dom.style.overflow = "hidden";
54250     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54251     this.el.removeClass("x-layout-inactive-content");
54252     this.el.on("mousewheel", this.onWheel, this);
54253
54254     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54255     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54256     up.unselectable(); down.unselectable();
54257     up.on("click", this.scrollUp, this);
54258     down.on("click", this.scrollDown, this);
54259     up.addClassOnOver("x-scroller-btn-over");
54260     down.addClassOnOver("x-scroller-btn-over");
54261     up.addClassOnClick("x-scroller-btn-click");
54262     down.addClassOnClick("x-scroller-btn-click");
54263     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54264
54265     this.resizeEl = this.el;
54266     this.el = wrap; this.up = up; this.down = down;
54267 };
54268
54269 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54270     increment : 100,
54271     wheelIncrement : 5,
54272     scrollUp : function(){
54273         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54274     },
54275
54276     scrollDown : function(){
54277         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54278     },
54279
54280     afterScroll : function(){
54281         var el = this.resizeEl;
54282         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54283         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54284         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54285     },
54286
54287     setSize : function(){
54288         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54289         this.afterScroll();
54290     },
54291
54292     onWheel : function(e){
54293         var d = e.getWheelDelta();
54294         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54295         this.afterScroll();
54296         e.stopEvent();
54297     },
54298
54299     setContent : function(content, loadScripts){
54300         this.resizeEl.update(content, loadScripts);
54301     }
54302
54303 });
54304
54305
54306
54307
54308
54309
54310
54311
54312
54313 /**
54314  * @class Roo.TreePanel
54315  * @extends Roo.ContentPanel
54316  * @constructor
54317  * Create a new TreePanel. - defaults to fit/scoll contents.
54318  * @param {String/Object} config A string to set only the panel's title, or a config object
54319  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54320  */
54321 Roo.TreePanel = function(config){
54322     var el = config.el;
54323     var tree = config.tree;
54324     delete config.tree; 
54325     delete config.el; // hopefull!
54326     
54327     // wrapper for IE7 strict & safari scroll issue
54328     
54329     var treeEl = el.createChild();
54330     config.resizeEl = treeEl;
54331     
54332     
54333     
54334     Roo.TreePanel.superclass.constructor.call(this, el, config);
54335  
54336  
54337     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54338     //console.log(tree);
54339     this.on('activate', function()
54340     {
54341         if (this.tree.rendered) {
54342             return;
54343         }
54344         //console.log('render tree');
54345         this.tree.render();
54346     });
54347     // this should not be needed.. - it's actually the 'el' that resizes?
54348     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54349     
54350     //this.on('resize',  function (cp, w, h) {
54351     //        this.tree.innerCt.setWidth(w);
54352     //        this.tree.innerCt.setHeight(h);
54353     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54354     //});
54355
54356         
54357     
54358 };
54359
54360 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54361     fitToFrame : true,
54362     autoScroll : true
54363 });
54364
54365
54366
54367
54368
54369
54370
54371
54372
54373
54374
54375 /*
54376  * Based on:
54377  * Ext JS Library 1.1.1
54378  * Copyright(c) 2006-2007, Ext JS, LLC.
54379  *
54380  * Originally Released Under LGPL - original licence link has changed is not relivant.
54381  *
54382  * Fork - LGPL
54383  * <script type="text/javascript">
54384  */
54385  
54386
54387 /**
54388  * @class Roo.ReaderLayout
54389  * @extends Roo.BorderLayout
54390  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54391  * center region containing two nested regions (a top one for a list view and one for item preview below),
54392  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54393  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54394  * expedites the setup of the overall layout and regions for this common application style.
54395  * Example:
54396  <pre><code>
54397 var reader = new Roo.ReaderLayout();
54398 var CP = Roo.ContentPanel;  // shortcut for adding
54399
54400 reader.beginUpdate();
54401 reader.add("north", new CP("north", "North"));
54402 reader.add("west", new CP("west", {title: "West"}));
54403 reader.add("east", new CP("east", {title: "East"}));
54404
54405 reader.regions.listView.add(new CP("listView", "List"));
54406 reader.regions.preview.add(new CP("preview", "Preview"));
54407 reader.endUpdate();
54408 </code></pre>
54409 * @constructor
54410 * Create a new ReaderLayout
54411 * @param {Object} config Configuration options
54412 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54413 * document.body if omitted)
54414 */
54415 Roo.ReaderLayout = function(config, renderTo){
54416     var c = config || {size:{}};
54417     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54418         north: c.north !== false ? Roo.apply({
54419             split:false,
54420             initialSize: 32,
54421             titlebar: false
54422         }, c.north) : false,
54423         west: c.west !== false ? Roo.apply({
54424             split:true,
54425             initialSize: 200,
54426             minSize: 175,
54427             maxSize: 400,
54428             titlebar: true,
54429             collapsible: true,
54430             animate: true,
54431             margins:{left:5,right:0,bottom:5,top:5},
54432             cmargins:{left:5,right:5,bottom:5,top:5}
54433         }, c.west) : false,
54434         east: c.east !== false ? Roo.apply({
54435             split:true,
54436             initialSize: 200,
54437             minSize: 175,
54438             maxSize: 400,
54439             titlebar: true,
54440             collapsible: true,
54441             animate: true,
54442             margins:{left:0,right:5,bottom:5,top:5},
54443             cmargins:{left:5,right:5,bottom:5,top:5}
54444         }, c.east) : false,
54445         center: Roo.apply({
54446             tabPosition: 'top',
54447             autoScroll:false,
54448             closeOnTab: true,
54449             titlebar:false,
54450             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54451         }, c.center)
54452     });
54453
54454     this.el.addClass('x-reader');
54455
54456     this.beginUpdate();
54457
54458     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54459         south: c.preview !== false ? Roo.apply({
54460             split:true,
54461             initialSize: 200,
54462             minSize: 100,
54463             autoScroll:true,
54464             collapsible:true,
54465             titlebar: true,
54466             cmargins:{top:5,left:0, right:0, bottom:0}
54467         }, c.preview) : false,
54468         center: Roo.apply({
54469             autoScroll:false,
54470             titlebar:false,
54471             minHeight:200
54472         }, c.listView)
54473     });
54474     this.add('center', new Roo.NestedLayoutPanel(inner,
54475             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54476
54477     this.endUpdate();
54478
54479     this.regions.preview = inner.getRegion('south');
54480     this.regions.listView = inner.getRegion('center');
54481 };
54482
54483 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54484  * Based on:
54485  * Ext JS Library 1.1.1
54486  * Copyright(c) 2006-2007, Ext JS, LLC.
54487  *
54488  * Originally Released Under LGPL - original licence link has changed is not relivant.
54489  *
54490  * Fork - LGPL
54491  * <script type="text/javascript">
54492  */
54493  
54494 /**
54495  * @class Roo.grid.Grid
54496  * @extends Roo.util.Observable
54497  * This class represents the primary interface of a component based grid control.
54498  * <br><br>Usage:<pre><code>
54499  var grid = new Roo.grid.Grid("my-container-id", {
54500      ds: myDataStore,
54501      cm: myColModel,
54502      selModel: mySelectionModel,
54503      autoSizeColumns: true,
54504      monitorWindowResize: false,
54505      trackMouseOver: true
54506  });
54507  // set any options
54508  grid.render();
54509  * </code></pre>
54510  * <b>Common Problems:</b><br/>
54511  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54512  * element will correct this<br/>
54513  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54514  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54515  * are unpredictable.<br/>
54516  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54517  * grid to calculate dimensions/offsets.<br/>
54518   * @constructor
54519  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54520  * The container MUST have some type of size defined for the grid to fill. The container will be
54521  * automatically set to position relative if it isn't already.
54522  * @param {Object} config A config object that sets properties on this grid.
54523  */
54524 Roo.grid.Grid = function(container, config){
54525         // initialize the container
54526         this.container = Roo.get(container);
54527         this.container.update("");
54528         this.container.setStyle("overflow", "hidden");
54529     this.container.addClass('x-grid-container');
54530
54531     this.id = this.container.id;
54532
54533     Roo.apply(this, config);
54534     // check and correct shorthanded configs
54535     if(this.ds){
54536         this.dataSource = this.ds;
54537         delete this.ds;
54538     }
54539     if(this.cm){
54540         this.colModel = this.cm;
54541         delete this.cm;
54542     }
54543     if(this.sm){
54544         this.selModel = this.sm;
54545         delete this.sm;
54546     }
54547
54548     if (this.selModel) {
54549         this.selModel = Roo.factory(this.selModel, Roo.grid);
54550         this.sm = this.selModel;
54551         this.sm.xmodule = this.xmodule || false;
54552     }
54553     if (typeof(this.colModel.config) == 'undefined') {
54554         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54555         this.cm = this.colModel;
54556         this.cm.xmodule = this.xmodule || false;
54557     }
54558     if (this.dataSource) {
54559         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54560         this.ds = this.dataSource;
54561         this.ds.xmodule = this.xmodule || false;
54562          
54563     }
54564     
54565     
54566     
54567     if(this.width){
54568         this.container.setWidth(this.width);
54569     }
54570
54571     if(this.height){
54572         this.container.setHeight(this.height);
54573     }
54574     /** @private */
54575         this.addEvents({
54576         // raw events
54577         /**
54578          * @event click
54579          * The raw click event for the entire grid.
54580          * @param {Roo.EventObject} e
54581          */
54582         "click" : true,
54583         /**
54584          * @event dblclick
54585          * The raw dblclick event for the entire grid.
54586          * @param {Roo.EventObject} e
54587          */
54588         "dblclick" : true,
54589         /**
54590          * @event contextmenu
54591          * The raw contextmenu event for the entire grid.
54592          * @param {Roo.EventObject} e
54593          */
54594         "contextmenu" : true,
54595         /**
54596          * @event mousedown
54597          * The raw mousedown event for the entire grid.
54598          * @param {Roo.EventObject} e
54599          */
54600         "mousedown" : true,
54601         /**
54602          * @event mouseup
54603          * The raw mouseup event for the entire grid.
54604          * @param {Roo.EventObject} e
54605          */
54606         "mouseup" : true,
54607         /**
54608          * @event mouseover
54609          * The raw mouseover event for the entire grid.
54610          * @param {Roo.EventObject} e
54611          */
54612         "mouseover" : true,
54613         /**
54614          * @event mouseout
54615          * The raw mouseout event for the entire grid.
54616          * @param {Roo.EventObject} e
54617          */
54618         "mouseout" : true,
54619         /**
54620          * @event keypress
54621          * The raw keypress event for the entire grid.
54622          * @param {Roo.EventObject} e
54623          */
54624         "keypress" : true,
54625         /**
54626          * @event keydown
54627          * The raw keydown event for the entire grid.
54628          * @param {Roo.EventObject} e
54629          */
54630         "keydown" : true,
54631
54632         // custom events
54633
54634         /**
54635          * @event cellclick
54636          * Fires when a cell is clicked
54637          * @param {Grid} this
54638          * @param {Number} rowIndex
54639          * @param {Number} columnIndex
54640          * @param {Roo.EventObject} e
54641          */
54642         "cellclick" : true,
54643         /**
54644          * @event celldblclick
54645          * Fires when a cell is double clicked
54646          * @param {Grid} this
54647          * @param {Number} rowIndex
54648          * @param {Number} columnIndex
54649          * @param {Roo.EventObject} e
54650          */
54651         "celldblclick" : true,
54652         /**
54653          * @event rowclick
54654          * Fires when a row is clicked
54655          * @param {Grid} this
54656          * @param {Number} rowIndex
54657          * @param {Roo.EventObject} e
54658          */
54659         "rowclick" : true,
54660         /**
54661          * @event rowdblclick
54662          * Fires when a row is double clicked
54663          * @param {Grid} this
54664          * @param {Number} rowIndex
54665          * @param {Roo.EventObject} e
54666          */
54667         "rowdblclick" : true,
54668         /**
54669          * @event headerclick
54670          * Fires when a header is clicked
54671          * @param {Grid} this
54672          * @param {Number} columnIndex
54673          * @param {Roo.EventObject} e
54674          */
54675         "headerclick" : true,
54676         /**
54677          * @event headerdblclick
54678          * Fires when a header cell is double clicked
54679          * @param {Grid} this
54680          * @param {Number} columnIndex
54681          * @param {Roo.EventObject} e
54682          */
54683         "headerdblclick" : true,
54684         /**
54685          * @event rowcontextmenu
54686          * Fires when a row is right clicked
54687          * @param {Grid} this
54688          * @param {Number} rowIndex
54689          * @param {Roo.EventObject} e
54690          */
54691         "rowcontextmenu" : true,
54692         /**
54693          * @event cellcontextmenu
54694          * Fires when a cell is right clicked
54695          * @param {Grid} this
54696          * @param {Number} rowIndex
54697          * @param {Number} cellIndex
54698          * @param {Roo.EventObject} e
54699          */
54700          "cellcontextmenu" : true,
54701         /**
54702          * @event headercontextmenu
54703          * Fires when a header is right clicked
54704          * @param {Grid} this
54705          * @param {Number} columnIndex
54706          * @param {Roo.EventObject} e
54707          */
54708         "headercontextmenu" : true,
54709         /**
54710          * @event bodyscroll
54711          * Fires when the body element is scrolled
54712          * @param {Number} scrollLeft
54713          * @param {Number} scrollTop
54714          */
54715         "bodyscroll" : true,
54716         /**
54717          * @event columnresize
54718          * Fires when the user resizes a column
54719          * @param {Number} columnIndex
54720          * @param {Number} newSize
54721          */
54722         "columnresize" : true,
54723         /**
54724          * @event columnmove
54725          * Fires when the user moves a column
54726          * @param {Number} oldIndex
54727          * @param {Number} newIndex
54728          */
54729         "columnmove" : true,
54730         /**
54731          * @event startdrag
54732          * Fires when row(s) start being dragged
54733          * @param {Grid} this
54734          * @param {Roo.GridDD} dd The drag drop object
54735          * @param {event} e The raw browser event
54736          */
54737         "startdrag" : true,
54738         /**
54739          * @event enddrag
54740          * Fires when a drag operation is complete
54741          * @param {Grid} this
54742          * @param {Roo.GridDD} dd The drag drop object
54743          * @param {event} e The raw browser event
54744          */
54745         "enddrag" : true,
54746         /**
54747          * @event dragdrop
54748          * Fires when dragged row(s) are dropped on a valid DD target
54749          * @param {Grid} this
54750          * @param {Roo.GridDD} dd The drag drop object
54751          * @param {String} targetId The target drag drop object
54752          * @param {event} e The raw browser event
54753          */
54754         "dragdrop" : true,
54755         /**
54756          * @event dragover
54757          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54758          * @param {Grid} this
54759          * @param {Roo.GridDD} dd The drag drop object
54760          * @param {String} targetId The target drag drop object
54761          * @param {event} e The raw browser event
54762          */
54763         "dragover" : true,
54764         /**
54765          * @event dragenter
54766          *  Fires when the dragged row(s) first cross another DD target while being dragged
54767          * @param {Grid} this
54768          * @param {Roo.GridDD} dd The drag drop object
54769          * @param {String} targetId The target drag drop object
54770          * @param {event} e The raw browser event
54771          */
54772         "dragenter" : true,
54773         /**
54774          * @event dragout
54775          * Fires when the dragged row(s) leave another DD target while being dragged
54776          * @param {Grid} this
54777          * @param {Roo.GridDD} dd The drag drop object
54778          * @param {String} targetId The target drag drop object
54779          * @param {event} e The raw browser event
54780          */
54781         "dragout" : true,
54782         /**
54783          * @event rowclass
54784          * Fires when a row is rendered, so you can change add a style to it.
54785          * @param {GridView} gridview   The grid view
54786          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54787          */
54788         'rowclass' : true,
54789
54790         /**
54791          * @event render
54792          * Fires when the grid is rendered
54793          * @param {Grid} grid
54794          */
54795         'render' : true
54796     });
54797
54798     Roo.grid.Grid.superclass.constructor.call(this);
54799 };
54800 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54801     
54802     /**
54803      * @cfg {String} ddGroup - drag drop group.
54804      */
54805
54806     /**
54807      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54808      */
54809     minColumnWidth : 25,
54810
54811     /**
54812      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54813      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54814      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54815      */
54816     autoSizeColumns : false,
54817
54818     /**
54819      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54820      */
54821     autoSizeHeaders : true,
54822
54823     /**
54824      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54825      */
54826     monitorWindowResize : true,
54827
54828     /**
54829      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54830      * rows measured to get a columns size. Default is 0 (all rows).
54831      */
54832     maxRowsToMeasure : 0,
54833
54834     /**
54835      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54836      */
54837     trackMouseOver : true,
54838
54839     /**
54840     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54841     */
54842     
54843     /**
54844     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54845     */
54846     enableDragDrop : false,
54847     
54848     /**
54849     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54850     */
54851     enableColumnMove : true,
54852     
54853     /**
54854     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54855     */
54856     enableColumnHide : true,
54857     
54858     /**
54859     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54860     */
54861     enableRowHeightSync : false,
54862     
54863     /**
54864     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54865     */
54866     stripeRows : true,
54867     
54868     /**
54869     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54870     */
54871     autoHeight : false,
54872
54873     /**
54874      * @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.
54875      */
54876     autoExpandColumn : false,
54877
54878     /**
54879     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54880     * Default is 50.
54881     */
54882     autoExpandMin : 50,
54883
54884     /**
54885     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54886     */
54887     autoExpandMax : 1000,
54888
54889     /**
54890     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54891     */
54892     view : null,
54893
54894     /**
54895     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54896     */
54897     loadMask : false,
54898     /**
54899     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54900     */
54901     dropTarget: false,
54902     
54903    
54904     
54905     // private
54906     rendered : false,
54907
54908     /**
54909     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54910     * of a fixed width. Default is false.
54911     */
54912     /**
54913     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54914     */
54915     /**
54916      * Called once after all setup has been completed and the grid is ready to be rendered.
54917      * @return {Roo.grid.Grid} this
54918      */
54919     render : function()
54920     {
54921         var c = this.container;
54922         // try to detect autoHeight/width mode
54923         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54924             this.autoHeight = true;
54925         }
54926         var view = this.getView();
54927         view.init(this);
54928
54929         c.on("click", this.onClick, this);
54930         c.on("dblclick", this.onDblClick, this);
54931         c.on("contextmenu", this.onContextMenu, this);
54932         c.on("keydown", this.onKeyDown, this);
54933         if (Roo.isTouch) {
54934             c.on("touchstart", this.onTouchStart, this);
54935         }
54936
54937         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54938
54939         this.getSelectionModel().init(this);
54940
54941         view.render();
54942
54943         if(this.loadMask){
54944             this.loadMask = new Roo.LoadMask(this.container,
54945                     Roo.apply({store:this.dataSource}, this.loadMask));
54946         }
54947         
54948         
54949         if (this.toolbar && this.toolbar.xtype) {
54950             this.toolbar.container = this.getView().getHeaderPanel(true);
54951             this.toolbar = new Roo.Toolbar(this.toolbar);
54952         }
54953         if (this.footer && this.footer.xtype) {
54954             this.footer.dataSource = this.getDataSource();
54955             this.footer.container = this.getView().getFooterPanel(true);
54956             this.footer = Roo.factory(this.footer, Roo);
54957         }
54958         if (this.dropTarget && this.dropTarget.xtype) {
54959             delete this.dropTarget.xtype;
54960             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54961         }
54962         
54963         
54964         this.rendered = true;
54965         this.fireEvent('render', this);
54966         return this;
54967     },
54968
54969         /**
54970          * Reconfigures the grid to use a different Store and Column Model.
54971          * The View will be bound to the new objects and refreshed.
54972          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54973          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54974          */
54975     reconfigure : function(dataSource, colModel){
54976         if(this.loadMask){
54977             this.loadMask.destroy();
54978             this.loadMask = new Roo.LoadMask(this.container,
54979                     Roo.apply({store:dataSource}, this.loadMask));
54980         }
54981         this.view.bind(dataSource, colModel);
54982         this.dataSource = dataSource;
54983         this.colModel = colModel;
54984         this.view.refresh(true);
54985     },
54986
54987     // private
54988     onKeyDown : function(e){
54989         this.fireEvent("keydown", e);
54990     },
54991
54992     /**
54993      * Destroy this grid.
54994      * @param {Boolean} removeEl True to remove the element
54995      */
54996     destroy : function(removeEl, keepListeners){
54997         if(this.loadMask){
54998             this.loadMask.destroy();
54999         }
55000         var c = this.container;
55001         c.removeAllListeners();
55002         this.view.destroy();
55003         this.colModel.purgeListeners();
55004         if(!keepListeners){
55005             this.purgeListeners();
55006         }
55007         c.update("");
55008         if(removeEl === true){
55009             c.remove();
55010         }
55011     },
55012
55013     // private
55014     processEvent : function(name, e){
55015         // does this fire select???
55016         //Roo.log('grid:processEvent '  + name);
55017         
55018         if (name != 'touchstart' ) {
55019             this.fireEvent(name, e);    
55020         }
55021         
55022         var t = e.getTarget();
55023         var v = this.view;
55024         var header = v.findHeaderIndex(t);
55025         if(header !== false){
55026             var ename = name == 'touchstart' ? 'click' : name;
55027              
55028             this.fireEvent("header" + ename, this, header, e);
55029         }else{
55030             var row = v.findRowIndex(t);
55031             var cell = v.findCellIndex(t);
55032             if (name == 'touchstart') {
55033                 // first touch is always a click.
55034                 // hopefull this happens after selection is updated.?
55035                 name = false;
55036                 
55037                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55038                     var cs = this.selModel.getSelectedCell();
55039                     if (row == cs[0] && cell == cs[1]){
55040                         name = 'dblclick';
55041                     }
55042                 }
55043                 if (typeof(this.selModel.getSelections) != 'undefined') {
55044                     var cs = this.selModel.getSelections();
55045                     var ds = this.dataSource;
55046                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55047                         name = 'dblclick';
55048                     }
55049                 }
55050                 if (!name) {
55051                     return;
55052                 }
55053             }
55054             
55055             
55056             if(row !== false){
55057                 this.fireEvent("row" + name, this, row, e);
55058                 if(cell !== false){
55059                     this.fireEvent("cell" + name, this, row, cell, e);
55060                 }
55061             }
55062         }
55063     },
55064
55065     // private
55066     onClick : function(e){
55067         this.processEvent("click", e);
55068     },
55069    // private
55070     onTouchStart : function(e){
55071         this.processEvent("touchstart", e);
55072     },
55073
55074     // private
55075     onContextMenu : function(e, t){
55076         this.processEvent("contextmenu", e);
55077     },
55078
55079     // private
55080     onDblClick : function(e){
55081         this.processEvent("dblclick", e);
55082     },
55083
55084     // private
55085     walkCells : function(row, col, step, fn, scope){
55086         var cm = this.colModel, clen = cm.getColumnCount();
55087         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55088         if(step < 0){
55089             if(col < 0){
55090                 row--;
55091                 first = false;
55092             }
55093             while(row >= 0){
55094                 if(!first){
55095                     col = clen-1;
55096                 }
55097                 first = false;
55098                 while(col >= 0){
55099                     if(fn.call(scope || this, row, col, cm) === true){
55100                         return [row, col];
55101                     }
55102                     col--;
55103                 }
55104                 row--;
55105             }
55106         } else {
55107             if(col >= clen){
55108                 row++;
55109                 first = false;
55110             }
55111             while(row < rlen){
55112                 if(!first){
55113                     col = 0;
55114                 }
55115                 first = false;
55116                 while(col < clen){
55117                     if(fn.call(scope || this, row, col, cm) === true){
55118                         return [row, col];
55119                     }
55120                     col++;
55121                 }
55122                 row++;
55123             }
55124         }
55125         return null;
55126     },
55127
55128     // private
55129     getSelections : function(){
55130         return this.selModel.getSelections();
55131     },
55132
55133     /**
55134      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55135      * but if manual update is required this method will initiate it.
55136      */
55137     autoSize : function(){
55138         if(this.rendered){
55139             this.view.layout();
55140             if(this.view.adjustForScroll){
55141                 this.view.adjustForScroll();
55142             }
55143         }
55144     },
55145
55146     /**
55147      * Returns the grid's underlying element.
55148      * @return {Element} The element
55149      */
55150     getGridEl : function(){
55151         return this.container;
55152     },
55153
55154     // private for compatibility, overridden by editor grid
55155     stopEditing : function(){},
55156
55157     /**
55158      * Returns the grid's SelectionModel.
55159      * @return {SelectionModel}
55160      */
55161     getSelectionModel : function(){
55162         if(!this.selModel){
55163             this.selModel = new Roo.grid.RowSelectionModel();
55164         }
55165         return this.selModel;
55166     },
55167
55168     /**
55169      * Returns the grid's DataSource.
55170      * @return {DataSource}
55171      */
55172     getDataSource : function(){
55173         return this.dataSource;
55174     },
55175
55176     /**
55177      * Returns the grid's ColumnModel.
55178      * @return {ColumnModel}
55179      */
55180     getColumnModel : function(){
55181         return this.colModel;
55182     },
55183
55184     /**
55185      * Returns the grid's GridView object.
55186      * @return {GridView}
55187      */
55188     getView : function(){
55189         if(!this.view){
55190             this.view = new Roo.grid.GridView(this.viewConfig);
55191         }
55192         return this.view;
55193     },
55194     /**
55195      * Called to get grid's drag proxy text, by default returns this.ddText.
55196      * @return {String}
55197      */
55198     getDragDropText : function(){
55199         var count = this.selModel.getCount();
55200         return String.format(this.ddText, count, count == 1 ? '' : 's');
55201     }
55202 });
55203 /**
55204  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55205  * %0 is replaced with the number of selected rows.
55206  * @type String
55207  */
55208 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55209  * Based on:
55210  * Ext JS Library 1.1.1
55211  * Copyright(c) 2006-2007, Ext JS, LLC.
55212  *
55213  * Originally Released Under LGPL - original licence link has changed is not relivant.
55214  *
55215  * Fork - LGPL
55216  * <script type="text/javascript">
55217  */
55218  
55219 Roo.grid.AbstractGridView = function(){
55220         this.grid = null;
55221         
55222         this.events = {
55223             "beforerowremoved" : true,
55224             "beforerowsinserted" : true,
55225             "beforerefresh" : true,
55226             "rowremoved" : true,
55227             "rowsinserted" : true,
55228             "rowupdated" : true,
55229             "refresh" : true
55230         };
55231     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55232 };
55233
55234 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55235     rowClass : "x-grid-row",
55236     cellClass : "x-grid-cell",
55237     tdClass : "x-grid-td",
55238     hdClass : "x-grid-hd",
55239     splitClass : "x-grid-hd-split",
55240     
55241     init: function(grid){
55242         this.grid = grid;
55243                 var cid = this.grid.getGridEl().id;
55244         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55245         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55246         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55247         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55248         },
55249         
55250     getColumnRenderers : function(){
55251         var renderers = [];
55252         var cm = this.grid.colModel;
55253         var colCount = cm.getColumnCount();
55254         for(var i = 0; i < colCount; i++){
55255             renderers[i] = cm.getRenderer(i);
55256         }
55257         return renderers;
55258     },
55259     
55260     getColumnIds : function(){
55261         var ids = [];
55262         var cm = this.grid.colModel;
55263         var colCount = cm.getColumnCount();
55264         for(var i = 0; i < colCount; i++){
55265             ids[i] = cm.getColumnId(i);
55266         }
55267         return ids;
55268     },
55269     
55270     getDataIndexes : function(){
55271         if(!this.indexMap){
55272             this.indexMap = this.buildIndexMap();
55273         }
55274         return this.indexMap.colToData;
55275     },
55276     
55277     getColumnIndexByDataIndex : function(dataIndex){
55278         if(!this.indexMap){
55279             this.indexMap = this.buildIndexMap();
55280         }
55281         return this.indexMap.dataToCol[dataIndex];
55282     },
55283     
55284     /**
55285      * Set a css style for a column dynamically. 
55286      * @param {Number} colIndex The index of the column
55287      * @param {String} name The css property name
55288      * @param {String} value The css value
55289      */
55290     setCSSStyle : function(colIndex, name, value){
55291         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55292         Roo.util.CSS.updateRule(selector, name, value);
55293     },
55294     
55295     generateRules : function(cm){
55296         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55297         Roo.util.CSS.removeStyleSheet(rulesId);
55298         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55299             var cid = cm.getColumnId(i);
55300             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55301                          this.tdSelector, cid, " {\n}\n",
55302                          this.hdSelector, cid, " {\n}\n",
55303                          this.splitSelector, cid, " {\n}\n");
55304         }
55305         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55306     }
55307 });/*
55308  * Based on:
55309  * Ext JS Library 1.1.1
55310  * Copyright(c) 2006-2007, Ext JS, LLC.
55311  *
55312  * Originally Released Under LGPL - original licence link has changed is not relivant.
55313  *
55314  * Fork - LGPL
55315  * <script type="text/javascript">
55316  */
55317
55318 // private
55319 // This is a support class used internally by the Grid components
55320 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55321     this.grid = grid;
55322     this.view = grid.getView();
55323     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55324     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55325     if(hd2){
55326         this.setHandleElId(Roo.id(hd));
55327         this.setOuterHandleElId(Roo.id(hd2));
55328     }
55329     this.scroll = false;
55330 };
55331 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55332     maxDragWidth: 120,
55333     getDragData : function(e){
55334         var t = Roo.lib.Event.getTarget(e);
55335         var h = this.view.findHeaderCell(t);
55336         if(h){
55337             return {ddel: h.firstChild, header:h};
55338         }
55339         return false;
55340     },
55341
55342     onInitDrag : function(e){
55343         this.view.headersDisabled = true;
55344         var clone = this.dragData.ddel.cloneNode(true);
55345         clone.id = Roo.id();
55346         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55347         this.proxy.update(clone);
55348         return true;
55349     },
55350
55351     afterValidDrop : function(){
55352         var v = this.view;
55353         setTimeout(function(){
55354             v.headersDisabled = false;
55355         }, 50);
55356     },
55357
55358     afterInvalidDrop : function(){
55359         var v = this.view;
55360         setTimeout(function(){
55361             v.headersDisabled = false;
55362         }, 50);
55363     }
55364 });
55365 /*
55366  * Based on:
55367  * Ext JS Library 1.1.1
55368  * Copyright(c) 2006-2007, Ext JS, LLC.
55369  *
55370  * Originally Released Under LGPL - original licence link has changed is not relivant.
55371  *
55372  * Fork - LGPL
55373  * <script type="text/javascript">
55374  */
55375 // private
55376 // This is a support class used internally by the Grid components
55377 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55378     this.grid = grid;
55379     this.view = grid.getView();
55380     // split the proxies so they don't interfere with mouse events
55381     this.proxyTop = Roo.DomHelper.append(document.body, {
55382         cls:"col-move-top", html:"&#160;"
55383     }, true);
55384     this.proxyBottom = Roo.DomHelper.append(document.body, {
55385         cls:"col-move-bottom", html:"&#160;"
55386     }, true);
55387     this.proxyTop.hide = this.proxyBottom.hide = function(){
55388         this.setLeftTop(-100,-100);
55389         this.setStyle("visibility", "hidden");
55390     };
55391     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55392     // temporarily disabled
55393     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55394     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55395 };
55396 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55397     proxyOffsets : [-4, -9],
55398     fly: Roo.Element.fly,
55399
55400     getTargetFromEvent : function(e){
55401         var t = Roo.lib.Event.getTarget(e);
55402         var cindex = this.view.findCellIndex(t);
55403         if(cindex !== false){
55404             return this.view.getHeaderCell(cindex);
55405         }
55406         return null;
55407     },
55408
55409     nextVisible : function(h){
55410         var v = this.view, cm = this.grid.colModel;
55411         h = h.nextSibling;
55412         while(h){
55413             if(!cm.isHidden(v.getCellIndex(h))){
55414                 return h;
55415             }
55416             h = h.nextSibling;
55417         }
55418         return null;
55419     },
55420
55421     prevVisible : function(h){
55422         var v = this.view, cm = this.grid.colModel;
55423         h = h.prevSibling;
55424         while(h){
55425             if(!cm.isHidden(v.getCellIndex(h))){
55426                 return h;
55427             }
55428             h = h.prevSibling;
55429         }
55430         return null;
55431     },
55432
55433     positionIndicator : function(h, n, e){
55434         var x = Roo.lib.Event.getPageX(e);
55435         var r = Roo.lib.Dom.getRegion(n.firstChild);
55436         var px, pt, py = r.top + this.proxyOffsets[1];
55437         if((r.right - x) <= (r.right-r.left)/2){
55438             px = r.right+this.view.borderWidth;
55439             pt = "after";
55440         }else{
55441             px = r.left;
55442             pt = "before";
55443         }
55444         var oldIndex = this.view.getCellIndex(h);
55445         var newIndex = this.view.getCellIndex(n);
55446
55447         if(this.grid.colModel.isFixed(newIndex)){
55448             return false;
55449         }
55450
55451         var locked = this.grid.colModel.isLocked(newIndex);
55452
55453         if(pt == "after"){
55454             newIndex++;
55455         }
55456         if(oldIndex < newIndex){
55457             newIndex--;
55458         }
55459         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55460             return false;
55461         }
55462         px +=  this.proxyOffsets[0];
55463         this.proxyTop.setLeftTop(px, py);
55464         this.proxyTop.show();
55465         if(!this.bottomOffset){
55466             this.bottomOffset = this.view.mainHd.getHeight();
55467         }
55468         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55469         this.proxyBottom.show();
55470         return pt;
55471     },
55472
55473     onNodeEnter : function(n, dd, e, data){
55474         if(data.header != n){
55475             this.positionIndicator(data.header, n, e);
55476         }
55477     },
55478
55479     onNodeOver : function(n, dd, e, data){
55480         var result = false;
55481         if(data.header != n){
55482             result = this.positionIndicator(data.header, n, e);
55483         }
55484         if(!result){
55485             this.proxyTop.hide();
55486             this.proxyBottom.hide();
55487         }
55488         return result ? this.dropAllowed : this.dropNotAllowed;
55489     },
55490
55491     onNodeOut : function(n, dd, e, data){
55492         this.proxyTop.hide();
55493         this.proxyBottom.hide();
55494     },
55495
55496     onNodeDrop : function(n, dd, e, data){
55497         var h = data.header;
55498         if(h != n){
55499             var cm = this.grid.colModel;
55500             var x = Roo.lib.Event.getPageX(e);
55501             var r = Roo.lib.Dom.getRegion(n.firstChild);
55502             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55503             var oldIndex = this.view.getCellIndex(h);
55504             var newIndex = this.view.getCellIndex(n);
55505             var locked = cm.isLocked(newIndex);
55506             if(pt == "after"){
55507                 newIndex++;
55508             }
55509             if(oldIndex < newIndex){
55510                 newIndex--;
55511             }
55512             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55513                 return false;
55514             }
55515             cm.setLocked(oldIndex, locked, true);
55516             cm.moveColumn(oldIndex, newIndex);
55517             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55518             return true;
55519         }
55520         return false;
55521     }
55522 });
55523 /*
55524  * Based on:
55525  * Ext JS Library 1.1.1
55526  * Copyright(c) 2006-2007, Ext JS, LLC.
55527  *
55528  * Originally Released Under LGPL - original licence link has changed is not relivant.
55529  *
55530  * Fork - LGPL
55531  * <script type="text/javascript">
55532  */
55533   
55534 /**
55535  * @class Roo.grid.GridView
55536  * @extends Roo.util.Observable
55537  *
55538  * @constructor
55539  * @param {Object} config
55540  */
55541 Roo.grid.GridView = function(config){
55542     Roo.grid.GridView.superclass.constructor.call(this);
55543     this.el = null;
55544
55545     Roo.apply(this, config);
55546 };
55547
55548 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55549
55550     unselectable :  'unselectable="on"',
55551     unselectableCls :  'x-unselectable',
55552     
55553     
55554     rowClass : "x-grid-row",
55555
55556     cellClass : "x-grid-col",
55557
55558     tdClass : "x-grid-td",
55559
55560     hdClass : "x-grid-hd",
55561
55562     splitClass : "x-grid-split",
55563
55564     sortClasses : ["sort-asc", "sort-desc"],
55565
55566     enableMoveAnim : false,
55567
55568     hlColor: "C3DAF9",
55569
55570     dh : Roo.DomHelper,
55571
55572     fly : Roo.Element.fly,
55573
55574     css : Roo.util.CSS,
55575
55576     borderWidth: 1,
55577
55578     splitOffset: 3,
55579
55580     scrollIncrement : 22,
55581
55582     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55583
55584     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55585
55586     bind : function(ds, cm){
55587         if(this.ds){
55588             this.ds.un("load", this.onLoad, this);
55589             this.ds.un("datachanged", this.onDataChange, this);
55590             this.ds.un("add", this.onAdd, this);
55591             this.ds.un("remove", this.onRemove, this);
55592             this.ds.un("update", this.onUpdate, this);
55593             this.ds.un("clear", this.onClear, this);
55594         }
55595         if(ds){
55596             ds.on("load", this.onLoad, this);
55597             ds.on("datachanged", this.onDataChange, this);
55598             ds.on("add", this.onAdd, this);
55599             ds.on("remove", this.onRemove, this);
55600             ds.on("update", this.onUpdate, this);
55601             ds.on("clear", this.onClear, this);
55602         }
55603         this.ds = ds;
55604
55605         if(this.cm){
55606             this.cm.un("widthchange", this.onColWidthChange, this);
55607             this.cm.un("headerchange", this.onHeaderChange, this);
55608             this.cm.un("hiddenchange", this.onHiddenChange, this);
55609             this.cm.un("columnmoved", this.onColumnMove, this);
55610             this.cm.un("columnlockchange", this.onColumnLock, this);
55611         }
55612         if(cm){
55613             this.generateRules(cm);
55614             cm.on("widthchange", this.onColWidthChange, this);
55615             cm.on("headerchange", this.onHeaderChange, this);
55616             cm.on("hiddenchange", this.onHiddenChange, this);
55617             cm.on("columnmoved", this.onColumnMove, this);
55618             cm.on("columnlockchange", this.onColumnLock, this);
55619         }
55620         this.cm = cm;
55621     },
55622
55623     init: function(grid){
55624         Roo.grid.GridView.superclass.init.call(this, grid);
55625
55626         this.bind(grid.dataSource, grid.colModel);
55627
55628         grid.on("headerclick", this.handleHeaderClick, this);
55629
55630         if(grid.trackMouseOver){
55631             grid.on("mouseover", this.onRowOver, this);
55632             grid.on("mouseout", this.onRowOut, this);
55633         }
55634         grid.cancelTextSelection = function(){};
55635         this.gridId = grid.id;
55636
55637         var tpls = this.templates || {};
55638
55639         if(!tpls.master){
55640             tpls.master = new Roo.Template(
55641                '<div class="x-grid" hidefocus="true">',
55642                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55643                   '<div class="x-grid-topbar"></div>',
55644                   '<div class="x-grid-scroller"><div></div></div>',
55645                   '<div class="x-grid-locked">',
55646                       '<div class="x-grid-header">{lockedHeader}</div>',
55647                       '<div class="x-grid-body">{lockedBody}</div>',
55648                   "</div>",
55649                   '<div class="x-grid-viewport">',
55650                       '<div class="x-grid-header">{header}</div>',
55651                       '<div class="x-grid-body">{body}</div>',
55652                   "</div>",
55653                   '<div class="x-grid-bottombar"></div>',
55654                  
55655                   '<div class="x-grid-resize-proxy">&#160;</div>',
55656                "</div>"
55657             );
55658             tpls.master.disableformats = true;
55659         }
55660
55661         if(!tpls.header){
55662             tpls.header = new Roo.Template(
55663                '<table border="0" cellspacing="0" cellpadding="0">',
55664                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55665                "</table>{splits}"
55666             );
55667             tpls.header.disableformats = true;
55668         }
55669         tpls.header.compile();
55670
55671         if(!tpls.hcell){
55672             tpls.hcell = new Roo.Template(
55673                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55674                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55675                 "</div></td>"
55676              );
55677              tpls.hcell.disableFormats = true;
55678         }
55679         tpls.hcell.compile();
55680
55681         if(!tpls.hsplit){
55682             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55683                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55684             tpls.hsplit.disableFormats = true;
55685         }
55686         tpls.hsplit.compile();
55687
55688         if(!tpls.body){
55689             tpls.body = new Roo.Template(
55690                '<table border="0" cellspacing="0" cellpadding="0">',
55691                "<tbody>{rows}</tbody>",
55692                "</table>"
55693             );
55694             tpls.body.disableFormats = true;
55695         }
55696         tpls.body.compile();
55697
55698         if(!tpls.row){
55699             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55700             tpls.row.disableFormats = true;
55701         }
55702         tpls.row.compile();
55703
55704         if(!tpls.cell){
55705             tpls.cell = new Roo.Template(
55706                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55707                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55708                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55709                 "</td>"
55710             );
55711             tpls.cell.disableFormats = true;
55712         }
55713         tpls.cell.compile();
55714
55715         this.templates = tpls;
55716     },
55717
55718     // remap these for backwards compat
55719     onColWidthChange : function(){
55720         this.updateColumns.apply(this, arguments);
55721     },
55722     onHeaderChange : function(){
55723         this.updateHeaders.apply(this, arguments);
55724     }, 
55725     onHiddenChange : function(){
55726         this.handleHiddenChange.apply(this, arguments);
55727     },
55728     onColumnMove : function(){
55729         this.handleColumnMove.apply(this, arguments);
55730     },
55731     onColumnLock : function(){
55732         this.handleLockChange.apply(this, arguments);
55733     },
55734
55735     onDataChange : function(){
55736         this.refresh();
55737         this.updateHeaderSortState();
55738     },
55739
55740     onClear : function(){
55741         this.refresh();
55742     },
55743
55744     onUpdate : function(ds, record){
55745         this.refreshRow(record);
55746     },
55747
55748     refreshRow : function(record){
55749         var ds = this.ds, index;
55750         if(typeof record == 'number'){
55751             index = record;
55752             record = ds.getAt(index);
55753         }else{
55754             index = ds.indexOf(record);
55755         }
55756         this.insertRows(ds, index, index, true);
55757         this.onRemove(ds, record, index+1, true);
55758         this.syncRowHeights(index, index);
55759         this.layout();
55760         this.fireEvent("rowupdated", this, index, record);
55761     },
55762
55763     onAdd : function(ds, records, index){
55764         this.insertRows(ds, index, index + (records.length-1));
55765     },
55766
55767     onRemove : function(ds, record, index, isUpdate){
55768         if(isUpdate !== true){
55769             this.fireEvent("beforerowremoved", this, index, record);
55770         }
55771         var bt = this.getBodyTable(), lt = this.getLockedTable();
55772         if(bt.rows[index]){
55773             bt.firstChild.removeChild(bt.rows[index]);
55774         }
55775         if(lt.rows[index]){
55776             lt.firstChild.removeChild(lt.rows[index]);
55777         }
55778         if(isUpdate !== true){
55779             this.stripeRows(index);
55780             this.syncRowHeights(index, index);
55781             this.layout();
55782             this.fireEvent("rowremoved", this, index, record);
55783         }
55784     },
55785
55786     onLoad : function(){
55787         this.scrollToTop();
55788     },
55789
55790     /**
55791      * Scrolls the grid to the top
55792      */
55793     scrollToTop : function(){
55794         if(this.scroller){
55795             this.scroller.dom.scrollTop = 0;
55796             this.syncScroll();
55797         }
55798     },
55799
55800     /**
55801      * Gets a panel in the header of the grid that can be used for toolbars etc.
55802      * After modifying the contents of this panel a call to grid.autoSize() may be
55803      * required to register any changes in size.
55804      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55805      * @return Roo.Element
55806      */
55807     getHeaderPanel : function(doShow){
55808         if(doShow){
55809             this.headerPanel.show();
55810         }
55811         return this.headerPanel;
55812     },
55813
55814     /**
55815      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55816      * After modifying the contents of this panel a call to grid.autoSize() may be
55817      * required to register any changes in size.
55818      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55819      * @return Roo.Element
55820      */
55821     getFooterPanel : function(doShow){
55822         if(doShow){
55823             this.footerPanel.show();
55824         }
55825         return this.footerPanel;
55826     },
55827
55828     initElements : function(){
55829         var E = Roo.Element;
55830         var el = this.grid.getGridEl().dom.firstChild;
55831         var cs = el.childNodes;
55832
55833         this.el = new E(el);
55834         
55835          this.focusEl = new E(el.firstChild);
55836         this.focusEl.swallowEvent("click", true);
55837         
55838         this.headerPanel = new E(cs[1]);
55839         this.headerPanel.enableDisplayMode("block");
55840
55841         this.scroller = new E(cs[2]);
55842         this.scrollSizer = new E(this.scroller.dom.firstChild);
55843
55844         this.lockedWrap = new E(cs[3]);
55845         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55846         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55847
55848         this.mainWrap = new E(cs[4]);
55849         this.mainHd = new E(this.mainWrap.dom.firstChild);
55850         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55851
55852         this.footerPanel = new E(cs[5]);
55853         this.footerPanel.enableDisplayMode("block");
55854
55855         this.resizeProxy = new E(cs[6]);
55856
55857         this.headerSelector = String.format(
55858            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55859            this.lockedHd.id, this.mainHd.id
55860         );
55861
55862         this.splitterSelector = String.format(
55863            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55864            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55865         );
55866     },
55867     idToCssName : function(s)
55868     {
55869         return s.replace(/[^a-z0-9]+/ig, '-');
55870     },
55871
55872     getHeaderCell : function(index){
55873         return Roo.DomQuery.select(this.headerSelector)[index];
55874     },
55875
55876     getHeaderCellMeasure : function(index){
55877         return this.getHeaderCell(index).firstChild;
55878     },
55879
55880     getHeaderCellText : function(index){
55881         return this.getHeaderCell(index).firstChild.firstChild;
55882     },
55883
55884     getLockedTable : function(){
55885         return this.lockedBody.dom.firstChild;
55886     },
55887
55888     getBodyTable : function(){
55889         return this.mainBody.dom.firstChild;
55890     },
55891
55892     getLockedRow : function(index){
55893         return this.getLockedTable().rows[index];
55894     },
55895
55896     getRow : function(index){
55897         return this.getBodyTable().rows[index];
55898     },
55899
55900     getRowComposite : function(index){
55901         if(!this.rowEl){
55902             this.rowEl = new Roo.CompositeElementLite();
55903         }
55904         var els = [], lrow, mrow;
55905         if(lrow = this.getLockedRow(index)){
55906             els.push(lrow);
55907         }
55908         if(mrow = this.getRow(index)){
55909             els.push(mrow);
55910         }
55911         this.rowEl.elements = els;
55912         return this.rowEl;
55913     },
55914     /**
55915      * Gets the 'td' of the cell
55916      * 
55917      * @param {Integer} rowIndex row to select
55918      * @param {Integer} colIndex column to select
55919      * 
55920      * @return {Object} 
55921      */
55922     getCell : function(rowIndex, colIndex){
55923         var locked = this.cm.getLockedCount();
55924         var source;
55925         if(colIndex < locked){
55926             source = this.lockedBody.dom.firstChild;
55927         }else{
55928             source = this.mainBody.dom.firstChild;
55929             colIndex -= locked;
55930         }
55931         return source.rows[rowIndex].childNodes[colIndex];
55932     },
55933
55934     getCellText : function(rowIndex, colIndex){
55935         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55936     },
55937
55938     getCellBox : function(cell){
55939         var b = this.fly(cell).getBox();
55940         if(Roo.isOpera){ // opera fails to report the Y
55941             b.y = cell.offsetTop + this.mainBody.getY();
55942         }
55943         return b;
55944     },
55945
55946     getCellIndex : function(cell){
55947         var id = String(cell.className).match(this.cellRE);
55948         if(id){
55949             return parseInt(id[1], 10);
55950         }
55951         return 0;
55952     },
55953
55954     findHeaderIndex : function(n){
55955         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55956         return r ? this.getCellIndex(r) : false;
55957     },
55958
55959     findHeaderCell : function(n){
55960         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55961         return r ? r : false;
55962     },
55963
55964     findRowIndex : function(n){
55965         if(!n){
55966             return false;
55967         }
55968         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55969         return r ? r.rowIndex : false;
55970     },
55971
55972     findCellIndex : function(node){
55973         var stop = this.el.dom;
55974         while(node && node != stop){
55975             if(this.findRE.test(node.className)){
55976                 return this.getCellIndex(node);
55977             }
55978             node = node.parentNode;
55979         }
55980         return false;
55981     },
55982
55983     getColumnId : function(index){
55984         return this.cm.getColumnId(index);
55985     },
55986
55987     getSplitters : function()
55988     {
55989         if(this.splitterSelector){
55990            return Roo.DomQuery.select(this.splitterSelector);
55991         }else{
55992             return null;
55993       }
55994     },
55995
55996     getSplitter : function(index){
55997         return this.getSplitters()[index];
55998     },
55999
56000     onRowOver : function(e, t){
56001         var row;
56002         if((row = this.findRowIndex(t)) !== false){
56003             this.getRowComposite(row).addClass("x-grid-row-over");
56004         }
56005     },
56006
56007     onRowOut : function(e, t){
56008         var row;
56009         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56010             this.getRowComposite(row).removeClass("x-grid-row-over");
56011         }
56012     },
56013
56014     renderHeaders : function(){
56015         var cm = this.cm;
56016         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56017         var cb = [], lb = [], sb = [], lsb = [], p = {};
56018         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56019             p.cellId = "x-grid-hd-0-" + i;
56020             p.splitId = "x-grid-csplit-0-" + i;
56021             p.id = cm.getColumnId(i);
56022             p.value = cm.getColumnHeader(i) || "";
56023             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56024             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56025             if(!cm.isLocked(i)){
56026                 cb[cb.length] = ct.apply(p);
56027                 sb[sb.length] = st.apply(p);
56028             }else{
56029                 lb[lb.length] = ct.apply(p);
56030                 lsb[lsb.length] = st.apply(p);
56031             }
56032         }
56033         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56034                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56035     },
56036
56037     updateHeaders : function(){
56038         var html = this.renderHeaders();
56039         this.lockedHd.update(html[0]);
56040         this.mainHd.update(html[1]);
56041     },
56042
56043     /**
56044      * Focuses the specified row.
56045      * @param {Number} row The row index
56046      */
56047     focusRow : function(row)
56048     {
56049         //Roo.log('GridView.focusRow');
56050         var x = this.scroller.dom.scrollLeft;
56051         this.focusCell(row, 0, false);
56052         this.scroller.dom.scrollLeft = x;
56053     },
56054
56055     /**
56056      * Focuses the specified cell.
56057      * @param {Number} row The row index
56058      * @param {Number} col The column index
56059      * @param {Boolean} hscroll false to disable horizontal scrolling
56060      */
56061     focusCell : function(row, col, hscroll)
56062     {
56063         //Roo.log('GridView.focusCell');
56064         var el = this.ensureVisible(row, col, hscroll);
56065         this.focusEl.alignTo(el, "tl-tl");
56066         if(Roo.isGecko){
56067             this.focusEl.focus();
56068         }else{
56069             this.focusEl.focus.defer(1, this.focusEl);
56070         }
56071     },
56072
56073     /**
56074      * Scrolls the specified cell into view
56075      * @param {Number} row The row index
56076      * @param {Number} col The column index
56077      * @param {Boolean} hscroll false to disable horizontal scrolling
56078      */
56079     ensureVisible : function(row, col, hscroll)
56080     {
56081         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56082         //return null; //disable for testing.
56083         if(typeof row != "number"){
56084             row = row.rowIndex;
56085         }
56086         if(row < 0 && row >= this.ds.getCount()){
56087             return  null;
56088         }
56089         col = (col !== undefined ? col : 0);
56090         var cm = this.grid.colModel;
56091         while(cm.isHidden(col)){
56092             col++;
56093         }
56094
56095         var el = this.getCell(row, col);
56096         if(!el){
56097             return null;
56098         }
56099         var c = this.scroller.dom;
56100
56101         var ctop = parseInt(el.offsetTop, 10);
56102         var cleft = parseInt(el.offsetLeft, 10);
56103         var cbot = ctop + el.offsetHeight;
56104         var cright = cleft + el.offsetWidth;
56105         
56106         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56107         var stop = parseInt(c.scrollTop, 10);
56108         var sleft = parseInt(c.scrollLeft, 10);
56109         var sbot = stop + ch;
56110         var sright = sleft + c.clientWidth;
56111         /*
56112         Roo.log('GridView.ensureVisible:' +
56113                 ' ctop:' + ctop +
56114                 ' c.clientHeight:' + c.clientHeight +
56115                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56116                 ' stop:' + stop +
56117                 ' cbot:' + cbot +
56118                 ' sbot:' + sbot +
56119                 ' ch:' + ch  
56120                 );
56121         */
56122         if(ctop < stop){
56123              c.scrollTop = ctop;
56124             //Roo.log("set scrolltop to ctop DISABLE?");
56125         }else if(cbot > sbot){
56126             //Roo.log("set scrolltop to cbot-ch");
56127             c.scrollTop = cbot-ch;
56128         }
56129         
56130         if(hscroll !== false){
56131             if(cleft < sleft){
56132                 c.scrollLeft = cleft;
56133             }else if(cright > sright){
56134                 c.scrollLeft = cright-c.clientWidth;
56135             }
56136         }
56137          
56138         return el;
56139     },
56140
56141     updateColumns : function(){
56142         this.grid.stopEditing();
56143         var cm = this.grid.colModel, colIds = this.getColumnIds();
56144         //var totalWidth = cm.getTotalWidth();
56145         var pos = 0;
56146         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56147             //if(cm.isHidden(i)) continue;
56148             var w = cm.getColumnWidth(i);
56149             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56150             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56151         }
56152         this.updateSplitters();
56153     },
56154
56155     generateRules : function(cm){
56156         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56157         Roo.util.CSS.removeStyleSheet(rulesId);
56158         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56159             var cid = cm.getColumnId(i);
56160             var align = '';
56161             if(cm.config[i].align){
56162                 align = 'text-align:'+cm.config[i].align+';';
56163             }
56164             var hidden = '';
56165             if(cm.isHidden(i)){
56166                 hidden = 'display:none;';
56167             }
56168             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56169             ruleBuf.push(
56170                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56171                     this.hdSelector, cid, " {\n", align, width, "}\n",
56172                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56173                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56174         }
56175         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56176     },
56177
56178     updateSplitters : function(){
56179         var cm = this.cm, s = this.getSplitters();
56180         if(s){ // splitters not created yet
56181             var pos = 0, locked = true;
56182             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56183                 if(cm.isHidden(i)) {
56184                     continue;
56185                 }
56186                 var w = cm.getColumnWidth(i); // make sure it's a number
56187                 if(!cm.isLocked(i) && locked){
56188                     pos = 0;
56189                     locked = false;
56190                 }
56191                 pos += w;
56192                 s[i].style.left = (pos-this.splitOffset) + "px";
56193             }
56194         }
56195     },
56196
56197     handleHiddenChange : function(colModel, colIndex, hidden){
56198         if(hidden){
56199             this.hideColumn(colIndex);
56200         }else{
56201             this.unhideColumn(colIndex);
56202         }
56203     },
56204
56205     hideColumn : function(colIndex){
56206         var cid = this.getColumnId(colIndex);
56207         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56208         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56209         if(Roo.isSafari){
56210             this.updateHeaders();
56211         }
56212         this.updateSplitters();
56213         this.layout();
56214     },
56215
56216     unhideColumn : function(colIndex){
56217         var cid = this.getColumnId(colIndex);
56218         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56219         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56220
56221         if(Roo.isSafari){
56222             this.updateHeaders();
56223         }
56224         this.updateSplitters();
56225         this.layout();
56226     },
56227
56228     insertRows : function(dm, firstRow, lastRow, isUpdate){
56229         if(firstRow == 0 && lastRow == dm.getCount()-1){
56230             this.refresh();
56231         }else{
56232             if(!isUpdate){
56233                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56234             }
56235             var s = this.getScrollState();
56236             var markup = this.renderRows(firstRow, lastRow);
56237             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56238             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56239             this.restoreScroll(s);
56240             if(!isUpdate){
56241                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56242                 this.syncRowHeights(firstRow, lastRow);
56243                 this.stripeRows(firstRow);
56244                 this.layout();
56245             }
56246         }
56247     },
56248
56249     bufferRows : function(markup, target, index){
56250         var before = null, trows = target.rows, tbody = target.tBodies[0];
56251         if(index < trows.length){
56252             before = trows[index];
56253         }
56254         var b = document.createElement("div");
56255         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56256         var rows = b.firstChild.rows;
56257         for(var i = 0, len = rows.length; i < len; i++){
56258             if(before){
56259                 tbody.insertBefore(rows[0], before);
56260             }else{
56261                 tbody.appendChild(rows[0]);
56262             }
56263         }
56264         b.innerHTML = "";
56265         b = null;
56266     },
56267
56268     deleteRows : function(dm, firstRow, lastRow){
56269         if(dm.getRowCount()<1){
56270             this.fireEvent("beforerefresh", this);
56271             this.mainBody.update("");
56272             this.lockedBody.update("");
56273             this.fireEvent("refresh", this);
56274         }else{
56275             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56276             var bt = this.getBodyTable();
56277             var tbody = bt.firstChild;
56278             var rows = bt.rows;
56279             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56280                 tbody.removeChild(rows[firstRow]);
56281             }
56282             this.stripeRows(firstRow);
56283             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56284         }
56285     },
56286
56287     updateRows : function(dataSource, firstRow, lastRow){
56288         var s = this.getScrollState();
56289         this.refresh();
56290         this.restoreScroll(s);
56291     },
56292
56293     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56294         if(!noRefresh){
56295            this.refresh();
56296         }
56297         this.updateHeaderSortState();
56298     },
56299
56300     getScrollState : function(){
56301         
56302         var sb = this.scroller.dom;
56303         return {left: sb.scrollLeft, top: sb.scrollTop};
56304     },
56305
56306     stripeRows : function(startRow){
56307         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56308             return;
56309         }
56310         startRow = startRow || 0;
56311         var rows = this.getBodyTable().rows;
56312         var lrows = this.getLockedTable().rows;
56313         var cls = ' x-grid-row-alt ';
56314         for(var i = startRow, len = rows.length; i < len; i++){
56315             var row = rows[i], lrow = lrows[i];
56316             var isAlt = ((i+1) % 2 == 0);
56317             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56318             if(isAlt == hasAlt){
56319                 continue;
56320             }
56321             if(isAlt){
56322                 row.className += " x-grid-row-alt";
56323             }else{
56324                 row.className = row.className.replace("x-grid-row-alt", "");
56325             }
56326             if(lrow){
56327                 lrow.className = row.className;
56328             }
56329         }
56330     },
56331
56332     restoreScroll : function(state){
56333         //Roo.log('GridView.restoreScroll');
56334         var sb = this.scroller.dom;
56335         sb.scrollLeft = state.left;
56336         sb.scrollTop = state.top;
56337         this.syncScroll();
56338     },
56339
56340     syncScroll : function(){
56341         //Roo.log('GridView.syncScroll');
56342         var sb = this.scroller.dom;
56343         var sh = this.mainHd.dom;
56344         var bs = this.mainBody.dom;
56345         var lv = this.lockedBody.dom;
56346         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56347         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56348     },
56349
56350     handleScroll : function(e){
56351         this.syncScroll();
56352         var sb = this.scroller.dom;
56353         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56354         e.stopEvent();
56355     },
56356
56357     handleWheel : function(e){
56358         var d = e.getWheelDelta();
56359         this.scroller.dom.scrollTop -= d*22;
56360         // set this here to prevent jumpy scrolling on large tables
56361         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56362         e.stopEvent();
56363     },
56364
56365     renderRows : function(startRow, endRow){
56366         // pull in all the crap needed to render rows
56367         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56368         var colCount = cm.getColumnCount();
56369
56370         if(ds.getCount() < 1){
56371             return ["", ""];
56372         }
56373
56374         // build a map for all the columns
56375         var cs = [];
56376         for(var i = 0; i < colCount; i++){
56377             var name = cm.getDataIndex(i);
56378             cs[i] = {
56379                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56380                 renderer : cm.getRenderer(i),
56381                 id : cm.getColumnId(i),
56382                 locked : cm.isLocked(i),
56383                 has_editor : cm.isCellEditable(i)
56384             };
56385         }
56386
56387         startRow = startRow || 0;
56388         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56389
56390         // records to render
56391         var rs = ds.getRange(startRow, endRow);
56392
56393         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56394     },
56395
56396     // As much as I hate to duplicate code, this was branched because FireFox really hates
56397     // [].join("") on strings. The performance difference was substantial enough to
56398     // branch this function
56399     doRender : Roo.isGecko ?
56400             function(cs, rs, ds, startRow, colCount, stripe){
56401                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56402                 // buffers
56403                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56404                 
56405                 var hasListener = this.grid.hasListener('rowclass');
56406                 var rowcfg = {};
56407                 for(var j = 0, len = rs.length; j < len; j++){
56408                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56409                     for(var i = 0; i < colCount; i++){
56410                         c = cs[i];
56411                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56412                         p.id = c.id;
56413                         p.css = p.attr = "";
56414                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56415                         if(p.value == undefined || p.value === "") {
56416                             p.value = "&#160;";
56417                         }
56418                         if(c.has_editor){
56419                             p.css += ' x-grid-editable-cell';
56420                         }
56421                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56422                             p.css +=  ' x-grid-dirty-cell';
56423                         }
56424                         var markup = ct.apply(p);
56425                         if(!c.locked){
56426                             cb+= markup;
56427                         }else{
56428                             lcb+= markup;
56429                         }
56430                     }
56431                     var alt = [];
56432                     if(stripe && ((rowIndex+1) % 2 == 0)){
56433                         alt.push("x-grid-row-alt")
56434                     }
56435                     if(r.dirty){
56436                         alt.push(  " x-grid-dirty-row");
56437                     }
56438                     rp.cells = lcb;
56439                     if(this.getRowClass){
56440                         alt.push(this.getRowClass(r, rowIndex));
56441                     }
56442                     if (hasListener) {
56443                         rowcfg = {
56444                              
56445                             record: r,
56446                             rowIndex : rowIndex,
56447                             rowClass : ''
56448                         };
56449                         this.grid.fireEvent('rowclass', this, rowcfg);
56450                         alt.push(rowcfg.rowClass);
56451                     }
56452                     rp.alt = alt.join(" ");
56453                     lbuf+= rt.apply(rp);
56454                     rp.cells = cb;
56455                     buf+=  rt.apply(rp);
56456                 }
56457                 return [lbuf, buf];
56458             } :
56459             function(cs, rs, ds, startRow, colCount, stripe){
56460                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56461                 // buffers
56462                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56463                 var hasListener = this.grid.hasListener('rowclass');
56464  
56465                 var rowcfg = {};
56466                 for(var j = 0, len = rs.length; j < len; j++){
56467                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56468                     for(var i = 0; i < colCount; i++){
56469                         c = cs[i];
56470                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56471                         p.id = c.id;
56472                         p.css = p.attr = "";
56473                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56474                         if(p.value == undefined || p.value === "") {
56475                             p.value = "&#160;";
56476                         }
56477                         //Roo.log(c);
56478                          if(c.has_editor){
56479                             p.css += ' x-grid-editable-cell';
56480                         }
56481                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56482                             p.css += ' x-grid-dirty-cell' 
56483                         }
56484                         
56485                         var markup = ct.apply(p);
56486                         if(!c.locked){
56487                             cb[cb.length] = markup;
56488                         }else{
56489                             lcb[lcb.length] = markup;
56490                         }
56491                     }
56492                     var alt = [];
56493                     if(stripe && ((rowIndex+1) % 2 == 0)){
56494                         alt.push( "x-grid-row-alt");
56495                     }
56496                     if(r.dirty){
56497                         alt.push(" x-grid-dirty-row");
56498                     }
56499                     rp.cells = lcb;
56500                     if(this.getRowClass){
56501                         alt.push( this.getRowClass(r, rowIndex));
56502                     }
56503                     if (hasListener) {
56504                         rowcfg = {
56505                              
56506                             record: r,
56507                             rowIndex : rowIndex,
56508                             rowClass : ''
56509                         };
56510                         this.grid.fireEvent('rowclass', this, rowcfg);
56511                         alt.push(rowcfg.rowClass);
56512                     }
56513                     
56514                     rp.alt = alt.join(" ");
56515                     rp.cells = lcb.join("");
56516                     lbuf[lbuf.length] = rt.apply(rp);
56517                     rp.cells = cb.join("");
56518                     buf[buf.length] =  rt.apply(rp);
56519                 }
56520                 return [lbuf.join(""), buf.join("")];
56521             },
56522
56523     renderBody : function(){
56524         var markup = this.renderRows();
56525         var bt = this.templates.body;
56526         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56527     },
56528
56529     /**
56530      * Refreshes the grid
56531      * @param {Boolean} headersToo
56532      */
56533     refresh : function(headersToo){
56534         this.fireEvent("beforerefresh", this);
56535         this.grid.stopEditing();
56536         var result = this.renderBody();
56537         this.lockedBody.update(result[0]);
56538         this.mainBody.update(result[1]);
56539         if(headersToo === true){
56540             this.updateHeaders();
56541             this.updateColumns();
56542             this.updateSplitters();
56543             this.updateHeaderSortState();
56544         }
56545         this.syncRowHeights();
56546         this.layout();
56547         this.fireEvent("refresh", this);
56548     },
56549
56550     handleColumnMove : function(cm, oldIndex, newIndex){
56551         this.indexMap = null;
56552         var s = this.getScrollState();
56553         this.refresh(true);
56554         this.restoreScroll(s);
56555         this.afterMove(newIndex);
56556     },
56557
56558     afterMove : function(colIndex){
56559         if(this.enableMoveAnim && Roo.enableFx){
56560             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56561         }
56562         // if multisort - fix sortOrder, and reload..
56563         if (this.grid.dataSource.multiSort) {
56564             // the we can call sort again..
56565             var dm = this.grid.dataSource;
56566             var cm = this.grid.colModel;
56567             var so = [];
56568             for(var i = 0; i < cm.config.length; i++ ) {
56569                 
56570                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56571                     continue; // dont' bother, it's not in sort list or being set.
56572                 }
56573                 
56574                 so.push(cm.config[i].dataIndex);
56575             };
56576             dm.sortOrder = so;
56577             dm.load(dm.lastOptions);
56578             
56579             
56580         }
56581         
56582     },
56583
56584     updateCell : function(dm, rowIndex, dataIndex){
56585         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56586         if(typeof colIndex == "undefined"){ // not present in grid
56587             return;
56588         }
56589         var cm = this.grid.colModel;
56590         var cell = this.getCell(rowIndex, colIndex);
56591         var cellText = this.getCellText(rowIndex, colIndex);
56592
56593         var p = {
56594             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56595             id : cm.getColumnId(colIndex),
56596             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56597         };
56598         var renderer = cm.getRenderer(colIndex);
56599         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56600         if(typeof val == "undefined" || val === "") {
56601             val = "&#160;";
56602         }
56603         cellText.innerHTML = val;
56604         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56605         this.syncRowHeights(rowIndex, rowIndex);
56606     },
56607
56608     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56609         var maxWidth = 0;
56610         if(this.grid.autoSizeHeaders){
56611             var h = this.getHeaderCellMeasure(colIndex);
56612             maxWidth = Math.max(maxWidth, h.scrollWidth);
56613         }
56614         var tb, index;
56615         if(this.cm.isLocked(colIndex)){
56616             tb = this.getLockedTable();
56617             index = colIndex;
56618         }else{
56619             tb = this.getBodyTable();
56620             index = colIndex - this.cm.getLockedCount();
56621         }
56622         if(tb && tb.rows){
56623             var rows = tb.rows;
56624             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56625             for(var i = 0; i < stopIndex; i++){
56626                 var cell = rows[i].childNodes[index].firstChild;
56627                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56628             }
56629         }
56630         return maxWidth + /*margin for error in IE*/ 5;
56631     },
56632     /**
56633      * Autofit a column to its content.
56634      * @param {Number} colIndex
56635      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56636      */
56637      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56638          if(this.cm.isHidden(colIndex)){
56639              return; // can't calc a hidden column
56640          }
56641         if(forceMinSize){
56642             var cid = this.cm.getColumnId(colIndex);
56643             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56644            if(this.grid.autoSizeHeaders){
56645                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56646            }
56647         }
56648         var newWidth = this.calcColumnWidth(colIndex);
56649         this.cm.setColumnWidth(colIndex,
56650             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56651         if(!suppressEvent){
56652             this.grid.fireEvent("columnresize", colIndex, newWidth);
56653         }
56654     },
56655
56656     /**
56657      * Autofits all columns to their content and then expands to fit any extra space in the grid
56658      */
56659      autoSizeColumns : function(){
56660         var cm = this.grid.colModel;
56661         var colCount = cm.getColumnCount();
56662         for(var i = 0; i < colCount; i++){
56663             this.autoSizeColumn(i, true, true);
56664         }
56665         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56666             this.fitColumns();
56667         }else{
56668             this.updateColumns();
56669             this.layout();
56670         }
56671     },
56672
56673     /**
56674      * Autofits all columns to the grid's width proportionate with their current size
56675      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56676      */
56677     fitColumns : function(reserveScrollSpace){
56678         var cm = this.grid.colModel;
56679         var colCount = cm.getColumnCount();
56680         var cols = [];
56681         var width = 0;
56682         var i, w;
56683         for (i = 0; i < colCount; i++){
56684             if(!cm.isHidden(i) && !cm.isFixed(i)){
56685                 w = cm.getColumnWidth(i);
56686                 cols.push(i);
56687                 cols.push(w);
56688                 width += w;
56689             }
56690         }
56691         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56692         if(reserveScrollSpace){
56693             avail -= 17;
56694         }
56695         var frac = (avail - cm.getTotalWidth())/width;
56696         while (cols.length){
56697             w = cols.pop();
56698             i = cols.pop();
56699             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56700         }
56701         this.updateColumns();
56702         this.layout();
56703     },
56704
56705     onRowSelect : function(rowIndex){
56706         var row = this.getRowComposite(rowIndex);
56707         row.addClass("x-grid-row-selected");
56708     },
56709
56710     onRowDeselect : function(rowIndex){
56711         var row = this.getRowComposite(rowIndex);
56712         row.removeClass("x-grid-row-selected");
56713     },
56714
56715     onCellSelect : function(row, col){
56716         var cell = this.getCell(row, col);
56717         if(cell){
56718             Roo.fly(cell).addClass("x-grid-cell-selected");
56719         }
56720     },
56721
56722     onCellDeselect : function(row, col){
56723         var cell = this.getCell(row, col);
56724         if(cell){
56725             Roo.fly(cell).removeClass("x-grid-cell-selected");
56726         }
56727     },
56728
56729     updateHeaderSortState : function(){
56730         
56731         // sort state can be single { field: xxx, direction : yyy}
56732         // or   { xxx=>ASC , yyy : DESC ..... }
56733         
56734         var mstate = {};
56735         if (!this.ds.multiSort) { 
56736             var state = this.ds.getSortState();
56737             if(!state){
56738                 return;
56739             }
56740             mstate[state.field] = state.direction;
56741             // FIXME... - this is not used here.. but might be elsewhere..
56742             this.sortState = state;
56743             
56744         } else {
56745             mstate = this.ds.sortToggle;
56746         }
56747         //remove existing sort classes..
56748         
56749         var sc = this.sortClasses;
56750         var hds = this.el.select(this.headerSelector).removeClass(sc);
56751         
56752         for(var f in mstate) {
56753         
56754             var sortColumn = this.cm.findColumnIndex(f);
56755             
56756             if(sortColumn != -1){
56757                 var sortDir = mstate[f];        
56758                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56759             }
56760         }
56761         
56762          
56763         
56764     },
56765
56766
56767     handleHeaderClick : function(g, index,e){
56768         
56769         Roo.log("header click");
56770         
56771         if (Roo.isTouch) {
56772             // touch events on header are handled by context
56773             this.handleHdCtx(g,index,e);
56774             return;
56775         }
56776         
56777         
56778         if(this.headersDisabled){
56779             return;
56780         }
56781         var dm = g.dataSource, cm = g.colModel;
56782         if(!cm.isSortable(index)){
56783             return;
56784         }
56785         g.stopEditing();
56786         
56787         if (dm.multiSort) {
56788             // update the sortOrder
56789             var so = [];
56790             for(var i = 0; i < cm.config.length; i++ ) {
56791                 
56792                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56793                     continue; // dont' bother, it's not in sort list or being set.
56794                 }
56795                 
56796                 so.push(cm.config[i].dataIndex);
56797             };
56798             dm.sortOrder = so;
56799         }
56800         
56801         
56802         dm.sort(cm.getDataIndex(index));
56803     },
56804
56805
56806     destroy : function(){
56807         if(this.colMenu){
56808             this.colMenu.removeAll();
56809             Roo.menu.MenuMgr.unregister(this.colMenu);
56810             this.colMenu.getEl().remove();
56811             delete this.colMenu;
56812         }
56813         if(this.hmenu){
56814             this.hmenu.removeAll();
56815             Roo.menu.MenuMgr.unregister(this.hmenu);
56816             this.hmenu.getEl().remove();
56817             delete this.hmenu;
56818         }
56819         if(this.grid.enableColumnMove){
56820             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56821             if(dds){
56822                 for(var dd in dds){
56823                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56824                         var elid = dds[dd].dragElId;
56825                         dds[dd].unreg();
56826                         Roo.get(elid).remove();
56827                     } else if(dds[dd].config.isTarget){
56828                         dds[dd].proxyTop.remove();
56829                         dds[dd].proxyBottom.remove();
56830                         dds[dd].unreg();
56831                     }
56832                     if(Roo.dd.DDM.locationCache[dd]){
56833                         delete Roo.dd.DDM.locationCache[dd];
56834                     }
56835                 }
56836                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56837             }
56838         }
56839         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56840         this.bind(null, null);
56841         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56842     },
56843
56844     handleLockChange : function(){
56845         this.refresh(true);
56846     },
56847
56848     onDenyColumnLock : function(){
56849
56850     },
56851
56852     onDenyColumnHide : function(){
56853
56854     },
56855
56856     handleHdMenuClick : function(item){
56857         var index = this.hdCtxIndex;
56858         var cm = this.cm, ds = this.ds;
56859         switch(item.id){
56860             case "asc":
56861                 ds.sort(cm.getDataIndex(index), "ASC");
56862                 break;
56863             case "desc":
56864                 ds.sort(cm.getDataIndex(index), "DESC");
56865                 break;
56866             case "lock":
56867                 var lc = cm.getLockedCount();
56868                 if(cm.getColumnCount(true) <= lc+1){
56869                     this.onDenyColumnLock();
56870                     return;
56871                 }
56872                 if(lc != index){
56873                     cm.setLocked(index, true, true);
56874                     cm.moveColumn(index, lc);
56875                     this.grid.fireEvent("columnmove", index, lc);
56876                 }else{
56877                     cm.setLocked(index, true);
56878                 }
56879             break;
56880             case "unlock":
56881                 var lc = cm.getLockedCount();
56882                 if((lc-1) != index){
56883                     cm.setLocked(index, false, true);
56884                     cm.moveColumn(index, lc-1);
56885                     this.grid.fireEvent("columnmove", index, lc-1);
56886                 }else{
56887                     cm.setLocked(index, false);
56888                 }
56889             break;
56890             case 'wider': // used to expand cols on touch..
56891             case 'narrow':
56892                 var cw = cm.getColumnWidth(index);
56893                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56894                 cw = Math.max(0, cw);
56895                 cw = Math.min(cw,4000);
56896                 cm.setColumnWidth(index, cw);
56897                 break;
56898                 
56899             default:
56900                 index = cm.getIndexById(item.id.substr(4));
56901                 if(index != -1){
56902                     if(item.checked && cm.getColumnCount(true) <= 1){
56903                         this.onDenyColumnHide();
56904                         return false;
56905                     }
56906                     cm.setHidden(index, item.checked);
56907                 }
56908         }
56909         return true;
56910     },
56911
56912     beforeColMenuShow : function(){
56913         var cm = this.cm,  colCount = cm.getColumnCount();
56914         this.colMenu.removeAll();
56915         for(var i = 0; i < colCount; i++){
56916             this.colMenu.add(new Roo.menu.CheckItem({
56917                 id: "col-"+cm.getColumnId(i),
56918                 text: cm.getColumnHeader(i),
56919                 checked: !cm.isHidden(i),
56920                 hideOnClick:false
56921             }));
56922         }
56923     },
56924
56925     handleHdCtx : function(g, index, e){
56926         e.stopEvent();
56927         var hd = this.getHeaderCell(index);
56928         this.hdCtxIndex = index;
56929         var ms = this.hmenu.items, cm = this.cm;
56930         ms.get("asc").setDisabled(!cm.isSortable(index));
56931         ms.get("desc").setDisabled(!cm.isSortable(index));
56932         if(this.grid.enableColLock !== false){
56933             ms.get("lock").setDisabled(cm.isLocked(index));
56934             ms.get("unlock").setDisabled(!cm.isLocked(index));
56935         }
56936         this.hmenu.show(hd, "tl-bl");
56937     },
56938
56939     handleHdOver : function(e){
56940         var hd = this.findHeaderCell(e.getTarget());
56941         if(hd && !this.headersDisabled){
56942             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56943                this.fly(hd).addClass("x-grid-hd-over");
56944             }
56945         }
56946     },
56947
56948     handleHdOut : function(e){
56949         var hd = this.findHeaderCell(e.getTarget());
56950         if(hd){
56951             this.fly(hd).removeClass("x-grid-hd-over");
56952         }
56953     },
56954
56955     handleSplitDblClick : function(e, t){
56956         var i = this.getCellIndex(t);
56957         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56958             this.autoSizeColumn(i, true);
56959             this.layout();
56960         }
56961     },
56962
56963     render : function(){
56964
56965         var cm = this.cm;
56966         var colCount = cm.getColumnCount();
56967
56968         if(this.grid.monitorWindowResize === true){
56969             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56970         }
56971         var header = this.renderHeaders();
56972         var body = this.templates.body.apply({rows:""});
56973         var html = this.templates.master.apply({
56974             lockedBody: body,
56975             body: body,
56976             lockedHeader: header[0],
56977             header: header[1]
56978         });
56979
56980         //this.updateColumns();
56981
56982         this.grid.getGridEl().dom.innerHTML = html;
56983
56984         this.initElements();
56985         
56986         // a kludge to fix the random scolling effect in webkit
56987         this.el.on("scroll", function() {
56988             this.el.dom.scrollTop=0; // hopefully not recursive..
56989         },this);
56990
56991         this.scroller.on("scroll", this.handleScroll, this);
56992         this.lockedBody.on("mousewheel", this.handleWheel, this);
56993         this.mainBody.on("mousewheel", this.handleWheel, this);
56994
56995         this.mainHd.on("mouseover", this.handleHdOver, this);
56996         this.mainHd.on("mouseout", this.handleHdOut, this);
56997         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56998                 {delegate: "."+this.splitClass});
56999
57000         this.lockedHd.on("mouseover", this.handleHdOver, this);
57001         this.lockedHd.on("mouseout", this.handleHdOut, this);
57002         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57003                 {delegate: "."+this.splitClass});
57004
57005         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57006             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57007         }
57008
57009         this.updateSplitters();
57010
57011         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57012             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57013             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57014         }
57015
57016         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57017             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57018             this.hmenu.add(
57019                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57020                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57021             );
57022             if(this.grid.enableColLock !== false){
57023                 this.hmenu.add('-',
57024                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57025                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57026                 );
57027             }
57028             if (Roo.isTouch) {
57029                  this.hmenu.add('-',
57030                     {id:"wider", text: this.columnsWiderText},
57031                     {id:"narrow", text: this.columnsNarrowText }
57032                 );
57033                 
57034                  
57035             }
57036             
57037             if(this.grid.enableColumnHide !== false){
57038
57039                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57040                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57041                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57042
57043                 this.hmenu.add('-',
57044                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57045                 );
57046             }
57047             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57048
57049             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57050         }
57051
57052         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57053             this.dd = new Roo.grid.GridDragZone(this.grid, {
57054                 ddGroup : this.grid.ddGroup || 'GridDD'
57055             });
57056             
57057         }
57058
57059         /*
57060         for(var i = 0; i < colCount; i++){
57061             if(cm.isHidden(i)){
57062                 this.hideColumn(i);
57063             }
57064             if(cm.config[i].align){
57065                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57066                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57067             }
57068         }*/
57069         
57070         this.updateHeaderSortState();
57071
57072         this.beforeInitialResize();
57073         this.layout(true);
57074
57075         // two part rendering gives faster view to the user
57076         this.renderPhase2.defer(1, this);
57077     },
57078
57079     renderPhase2 : function(){
57080         // render the rows now
57081         this.refresh();
57082         if(this.grid.autoSizeColumns){
57083             this.autoSizeColumns();
57084         }
57085     },
57086
57087     beforeInitialResize : function(){
57088
57089     },
57090
57091     onColumnSplitterMoved : function(i, w){
57092         this.userResized = true;
57093         var cm = this.grid.colModel;
57094         cm.setColumnWidth(i, w, true);
57095         var cid = cm.getColumnId(i);
57096         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57097         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57098         this.updateSplitters();
57099         this.layout();
57100         this.grid.fireEvent("columnresize", i, w);
57101     },
57102
57103     syncRowHeights : function(startIndex, endIndex){
57104         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57105             startIndex = startIndex || 0;
57106             var mrows = this.getBodyTable().rows;
57107             var lrows = this.getLockedTable().rows;
57108             var len = mrows.length-1;
57109             endIndex = Math.min(endIndex || len, len);
57110             for(var i = startIndex; i <= endIndex; i++){
57111                 var m = mrows[i], l = lrows[i];
57112                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57113                 m.style.height = l.style.height = h + "px";
57114             }
57115         }
57116     },
57117
57118     layout : function(initialRender, is2ndPass){
57119         var g = this.grid;
57120         var auto = g.autoHeight;
57121         var scrollOffset = 16;
57122         var c = g.getGridEl(), cm = this.cm,
57123                 expandCol = g.autoExpandColumn,
57124                 gv = this;
57125         //c.beginMeasure();
57126
57127         if(!c.dom.offsetWidth){ // display:none?
57128             if(initialRender){
57129                 this.lockedWrap.show();
57130                 this.mainWrap.show();
57131             }
57132             return;
57133         }
57134
57135         var hasLock = this.cm.isLocked(0);
57136
57137         var tbh = this.headerPanel.getHeight();
57138         var bbh = this.footerPanel.getHeight();
57139
57140         if(auto){
57141             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57142             var newHeight = ch + c.getBorderWidth("tb");
57143             if(g.maxHeight){
57144                 newHeight = Math.min(g.maxHeight, newHeight);
57145             }
57146             c.setHeight(newHeight);
57147         }
57148
57149         if(g.autoWidth){
57150             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57151         }
57152
57153         var s = this.scroller;
57154
57155         var csize = c.getSize(true);
57156
57157         this.el.setSize(csize.width, csize.height);
57158
57159         this.headerPanel.setWidth(csize.width);
57160         this.footerPanel.setWidth(csize.width);
57161
57162         var hdHeight = this.mainHd.getHeight();
57163         var vw = csize.width;
57164         var vh = csize.height - (tbh + bbh);
57165
57166         s.setSize(vw, vh);
57167
57168         var bt = this.getBodyTable();
57169         
57170         if(cm.getLockedCount() == cm.config.length){
57171             bt = this.getLockedTable();
57172         }
57173         
57174         var ltWidth = hasLock ?
57175                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57176
57177         var scrollHeight = bt.offsetHeight;
57178         var scrollWidth = ltWidth + bt.offsetWidth;
57179         var vscroll = false, hscroll = false;
57180
57181         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57182
57183         var lw = this.lockedWrap, mw = this.mainWrap;
57184         var lb = this.lockedBody, mb = this.mainBody;
57185
57186         setTimeout(function(){
57187             var t = s.dom.offsetTop;
57188             var w = s.dom.clientWidth,
57189                 h = s.dom.clientHeight;
57190
57191             lw.setTop(t);
57192             lw.setSize(ltWidth, h);
57193
57194             mw.setLeftTop(ltWidth, t);
57195             mw.setSize(w-ltWidth, h);
57196
57197             lb.setHeight(h-hdHeight);
57198             mb.setHeight(h-hdHeight);
57199
57200             if(is2ndPass !== true && !gv.userResized && expandCol){
57201                 // high speed resize without full column calculation
57202                 
57203                 var ci = cm.getIndexById(expandCol);
57204                 if (ci < 0) {
57205                     ci = cm.findColumnIndex(expandCol);
57206                 }
57207                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57208                 var expandId = cm.getColumnId(ci);
57209                 var  tw = cm.getTotalWidth(false);
57210                 var currentWidth = cm.getColumnWidth(ci);
57211                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57212                 if(currentWidth != cw){
57213                     cm.setColumnWidth(ci, cw, true);
57214                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57215                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57216                     gv.updateSplitters();
57217                     gv.layout(false, true);
57218                 }
57219             }
57220
57221             if(initialRender){
57222                 lw.show();
57223                 mw.show();
57224             }
57225             //c.endMeasure();
57226         }, 10);
57227     },
57228
57229     onWindowResize : function(){
57230         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57231             return;
57232         }
57233         this.layout();
57234     },
57235
57236     appendFooter : function(parentEl){
57237         return null;
57238     },
57239
57240     sortAscText : "Sort Ascending",
57241     sortDescText : "Sort Descending",
57242     lockText : "Lock Column",
57243     unlockText : "Unlock Column",
57244     columnsText : "Columns",
57245  
57246     columnsWiderText : "Wider",
57247     columnsNarrowText : "Thinner"
57248 });
57249
57250
57251 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57252     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57253     this.proxy.el.addClass('x-grid3-col-dd');
57254 };
57255
57256 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57257     handleMouseDown : function(e){
57258
57259     },
57260
57261     callHandleMouseDown : function(e){
57262         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57263     }
57264 });
57265 /*
57266  * Based on:
57267  * Ext JS Library 1.1.1
57268  * Copyright(c) 2006-2007, Ext JS, LLC.
57269  *
57270  * Originally Released Under LGPL - original licence link has changed is not relivant.
57271  *
57272  * Fork - LGPL
57273  * <script type="text/javascript">
57274  */
57275  
57276 // private
57277 // This is a support class used internally by the Grid components
57278 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57279     this.grid = grid;
57280     this.view = grid.getView();
57281     this.proxy = this.view.resizeProxy;
57282     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57283         "gridSplitters" + this.grid.getGridEl().id, {
57284         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57285     });
57286     this.setHandleElId(Roo.id(hd));
57287     this.setOuterHandleElId(Roo.id(hd2));
57288     this.scroll = false;
57289 };
57290 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57291     fly: Roo.Element.fly,
57292
57293     b4StartDrag : function(x, y){
57294         this.view.headersDisabled = true;
57295         this.proxy.setHeight(this.view.mainWrap.getHeight());
57296         var w = this.cm.getColumnWidth(this.cellIndex);
57297         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57298         this.resetConstraints();
57299         this.setXConstraint(minw, 1000);
57300         this.setYConstraint(0, 0);
57301         this.minX = x - minw;
57302         this.maxX = x + 1000;
57303         this.startPos = x;
57304         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57305     },
57306
57307
57308     handleMouseDown : function(e){
57309         ev = Roo.EventObject.setEvent(e);
57310         var t = this.fly(ev.getTarget());
57311         if(t.hasClass("x-grid-split")){
57312             this.cellIndex = this.view.getCellIndex(t.dom);
57313             this.split = t.dom;
57314             this.cm = this.grid.colModel;
57315             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57316                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57317             }
57318         }
57319     },
57320
57321     endDrag : function(e){
57322         this.view.headersDisabled = false;
57323         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57324         var diff = endX - this.startPos;
57325         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57326     },
57327
57328     autoOffset : function(){
57329         this.setDelta(0,0);
57330     }
57331 });/*
57332  * Based on:
57333  * Ext JS Library 1.1.1
57334  * Copyright(c) 2006-2007, Ext JS, LLC.
57335  *
57336  * Originally Released Under LGPL - original licence link has changed is not relivant.
57337  *
57338  * Fork - LGPL
57339  * <script type="text/javascript">
57340  */
57341  
57342 // private
57343 // This is a support class used internally by the Grid components
57344 Roo.grid.GridDragZone = function(grid, config){
57345     this.view = grid.getView();
57346     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57347     if(this.view.lockedBody){
57348         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57349         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57350     }
57351     this.scroll = false;
57352     this.grid = grid;
57353     this.ddel = document.createElement('div');
57354     this.ddel.className = 'x-grid-dd-wrap';
57355 };
57356
57357 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57358     ddGroup : "GridDD",
57359
57360     getDragData : function(e){
57361         var t = Roo.lib.Event.getTarget(e);
57362         var rowIndex = this.view.findRowIndex(t);
57363         var sm = this.grid.selModel;
57364             
57365         //Roo.log(rowIndex);
57366         
57367         if (sm.getSelectedCell) {
57368             // cell selection..
57369             if (!sm.getSelectedCell()) {
57370                 return false;
57371             }
57372             if (rowIndex != sm.getSelectedCell()[0]) {
57373                 return false;
57374             }
57375         
57376         }
57377         
57378         if(rowIndex !== false){
57379             
57380             // if editorgrid.. 
57381             
57382             
57383             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57384                
57385             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57386               //  
57387             //}
57388             if (e.hasModifier()){
57389                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57390             }
57391             
57392             Roo.log("getDragData");
57393             
57394             return {
57395                 grid: this.grid,
57396                 ddel: this.ddel,
57397                 rowIndex: rowIndex,
57398                 selections:sm.getSelections ? sm.getSelections() : (
57399                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57400                 )
57401             };
57402         }
57403         return false;
57404     },
57405
57406     onInitDrag : function(e){
57407         var data = this.dragData;
57408         this.ddel.innerHTML = this.grid.getDragDropText();
57409         this.proxy.update(this.ddel);
57410         // fire start drag?
57411     },
57412
57413     afterRepair : function(){
57414         this.dragging = false;
57415     },
57416
57417     getRepairXY : function(e, data){
57418         return false;
57419     },
57420
57421     onEndDrag : function(data, e){
57422         // fire end drag?
57423     },
57424
57425     onValidDrop : function(dd, e, id){
57426         // fire drag drop?
57427         this.hideProxy();
57428     },
57429
57430     beforeInvalidDrop : function(e, id){
57431
57432     }
57433 });/*
57434  * Based on:
57435  * Ext JS Library 1.1.1
57436  * Copyright(c) 2006-2007, Ext JS, LLC.
57437  *
57438  * Originally Released Under LGPL - original licence link has changed is not relivant.
57439  *
57440  * Fork - LGPL
57441  * <script type="text/javascript">
57442  */
57443  
57444
57445 /**
57446  * @class Roo.grid.ColumnModel
57447  * @extends Roo.util.Observable
57448  * This is the default implementation of a ColumnModel used by the Grid. It defines
57449  * the columns in the grid.
57450  * <br>Usage:<br>
57451  <pre><code>
57452  var colModel = new Roo.grid.ColumnModel([
57453         {header: "Ticker", width: 60, sortable: true, locked: true},
57454         {header: "Company Name", width: 150, sortable: true},
57455         {header: "Market Cap.", width: 100, sortable: true},
57456         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57457         {header: "Employees", width: 100, sortable: true, resizable: false}
57458  ]);
57459  </code></pre>
57460  * <p>
57461  
57462  * The config options listed for this class are options which may appear in each
57463  * individual column definition.
57464  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57465  * @constructor
57466  * @param {Object} config An Array of column config objects. See this class's
57467  * config objects for details.
57468 */
57469 Roo.grid.ColumnModel = function(config){
57470         /**
57471      * The config passed into the constructor
57472      */
57473     this.config = config;
57474     this.lookup = {};
57475
57476     // if no id, create one
57477     // if the column does not have a dataIndex mapping,
57478     // map it to the order it is in the config
57479     for(var i = 0, len = config.length; i < len; i++){
57480         var c = config[i];
57481         if(typeof c.dataIndex == "undefined"){
57482             c.dataIndex = i;
57483         }
57484         if(typeof c.renderer == "string"){
57485             c.renderer = Roo.util.Format[c.renderer];
57486         }
57487         if(typeof c.id == "undefined"){
57488             c.id = Roo.id();
57489         }
57490         if(c.editor && c.editor.xtype){
57491             c.editor  = Roo.factory(c.editor, Roo.grid);
57492         }
57493         if(c.editor && c.editor.isFormField){
57494             c.editor = new Roo.grid.GridEditor(c.editor);
57495         }
57496         this.lookup[c.id] = c;
57497     }
57498
57499     /**
57500      * The width of columns which have no width specified (defaults to 100)
57501      * @type Number
57502      */
57503     this.defaultWidth = 100;
57504
57505     /**
57506      * Default sortable of columns which have no sortable specified (defaults to false)
57507      * @type Boolean
57508      */
57509     this.defaultSortable = false;
57510
57511     this.addEvents({
57512         /**
57513              * @event widthchange
57514              * Fires when the width of a column changes.
57515              * @param {ColumnModel} this
57516              * @param {Number} columnIndex The column index
57517              * @param {Number} newWidth The new width
57518              */
57519             "widthchange": true,
57520         /**
57521              * @event headerchange
57522              * Fires when the text of a header changes.
57523              * @param {ColumnModel} this
57524              * @param {Number} columnIndex The column index
57525              * @param {Number} newText The new header text
57526              */
57527             "headerchange": true,
57528         /**
57529              * @event hiddenchange
57530              * Fires when a column is hidden or "unhidden".
57531              * @param {ColumnModel} this
57532              * @param {Number} columnIndex The column index
57533              * @param {Boolean} hidden true if hidden, false otherwise
57534              */
57535             "hiddenchange": true,
57536             /**
57537          * @event columnmoved
57538          * Fires when a column is moved.
57539          * @param {ColumnModel} this
57540          * @param {Number} oldIndex
57541          * @param {Number} newIndex
57542          */
57543         "columnmoved" : true,
57544         /**
57545          * @event columlockchange
57546          * Fires when a column's locked state is changed
57547          * @param {ColumnModel} this
57548          * @param {Number} colIndex
57549          * @param {Boolean} locked true if locked
57550          */
57551         "columnlockchange" : true
57552     });
57553     Roo.grid.ColumnModel.superclass.constructor.call(this);
57554 };
57555 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57556     /**
57557      * @cfg {String} header The header text to display in the Grid view.
57558      */
57559     /**
57560      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57561      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57562      * specified, the column's index is used as an index into the Record's data Array.
57563      */
57564     /**
57565      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57566      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57567      */
57568     /**
57569      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57570      * Defaults to the value of the {@link #defaultSortable} property.
57571      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57572      */
57573     /**
57574      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57575      */
57576     /**
57577      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57578      */
57579     /**
57580      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57581      */
57582     /**
57583      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57584      */
57585     /**
57586      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57587      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57588      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57589      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57590      */
57591        /**
57592      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57593      */
57594     /**
57595      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57596      */
57597     /**
57598      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57599      */
57600     /**
57601      * @cfg {String} cursor (Optional)
57602      */
57603     /**
57604      * @cfg {String} tooltip (Optional)
57605      */
57606     /**
57607      * @cfg {Number} xs (Optional)
57608      */
57609     /**
57610      * @cfg {Number} sm (Optional)
57611      */
57612     /**
57613      * @cfg {Number} md (Optional)
57614      */
57615     /**
57616      * @cfg {Number} lg (Optional)
57617      */
57618     /**
57619      * Returns the id of the column at the specified index.
57620      * @param {Number} index The column index
57621      * @return {String} the id
57622      */
57623     getColumnId : function(index){
57624         return this.config[index].id;
57625     },
57626
57627     /**
57628      * Returns the column for a specified id.
57629      * @param {String} id The column id
57630      * @return {Object} the column
57631      */
57632     getColumnById : function(id){
57633         return this.lookup[id];
57634     },
57635
57636     
57637     /**
57638      * Returns the column for a specified dataIndex.
57639      * @param {String} dataIndex The column dataIndex
57640      * @return {Object|Boolean} the column or false if not found
57641      */
57642     getColumnByDataIndex: function(dataIndex){
57643         var index = this.findColumnIndex(dataIndex);
57644         return index > -1 ? this.config[index] : false;
57645     },
57646     
57647     /**
57648      * Returns the index for a specified column id.
57649      * @param {String} id The column id
57650      * @return {Number} the index, or -1 if not found
57651      */
57652     getIndexById : function(id){
57653         for(var i = 0, len = this.config.length; i < len; i++){
57654             if(this.config[i].id == id){
57655                 return i;
57656             }
57657         }
57658         return -1;
57659     },
57660     
57661     /**
57662      * Returns the index for a specified column dataIndex.
57663      * @param {String} dataIndex The column dataIndex
57664      * @return {Number} the index, or -1 if not found
57665      */
57666     
57667     findColumnIndex : function(dataIndex){
57668         for(var i = 0, len = this.config.length; i < len; i++){
57669             if(this.config[i].dataIndex == dataIndex){
57670                 return i;
57671             }
57672         }
57673         return -1;
57674     },
57675     
57676     
57677     moveColumn : function(oldIndex, newIndex){
57678         var c = this.config[oldIndex];
57679         this.config.splice(oldIndex, 1);
57680         this.config.splice(newIndex, 0, c);
57681         this.dataMap = null;
57682         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57683     },
57684
57685     isLocked : function(colIndex){
57686         return this.config[colIndex].locked === true;
57687     },
57688
57689     setLocked : function(colIndex, value, suppressEvent){
57690         if(this.isLocked(colIndex) == value){
57691             return;
57692         }
57693         this.config[colIndex].locked = value;
57694         if(!suppressEvent){
57695             this.fireEvent("columnlockchange", this, colIndex, value);
57696         }
57697     },
57698
57699     getTotalLockedWidth : function(){
57700         var totalWidth = 0;
57701         for(var i = 0; i < this.config.length; i++){
57702             if(this.isLocked(i) && !this.isHidden(i)){
57703                 this.totalWidth += this.getColumnWidth(i);
57704             }
57705         }
57706         return totalWidth;
57707     },
57708
57709     getLockedCount : function(){
57710         for(var i = 0, len = this.config.length; i < len; i++){
57711             if(!this.isLocked(i)){
57712                 return i;
57713             }
57714         }
57715         
57716         return this.config.length;
57717     },
57718
57719     /**
57720      * Returns the number of columns.
57721      * @return {Number}
57722      */
57723     getColumnCount : function(visibleOnly){
57724         if(visibleOnly === true){
57725             var c = 0;
57726             for(var i = 0, len = this.config.length; i < len; i++){
57727                 if(!this.isHidden(i)){
57728                     c++;
57729                 }
57730             }
57731             return c;
57732         }
57733         return this.config.length;
57734     },
57735
57736     /**
57737      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57738      * @param {Function} fn
57739      * @param {Object} scope (optional)
57740      * @return {Array} result
57741      */
57742     getColumnsBy : function(fn, scope){
57743         var r = [];
57744         for(var i = 0, len = this.config.length; i < len; i++){
57745             var c = this.config[i];
57746             if(fn.call(scope||this, c, i) === true){
57747                 r[r.length] = c;
57748             }
57749         }
57750         return r;
57751     },
57752
57753     /**
57754      * Returns true if the specified column is sortable.
57755      * @param {Number} col The column index
57756      * @return {Boolean}
57757      */
57758     isSortable : function(col){
57759         if(typeof this.config[col].sortable == "undefined"){
57760             return this.defaultSortable;
57761         }
57762         return this.config[col].sortable;
57763     },
57764
57765     /**
57766      * Returns the rendering (formatting) function defined for the column.
57767      * @param {Number} col The column index.
57768      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57769      */
57770     getRenderer : function(col){
57771         if(!this.config[col].renderer){
57772             return Roo.grid.ColumnModel.defaultRenderer;
57773         }
57774         return this.config[col].renderer;
57775     },
57776
57777     /**
57778      * Sets the rendering (formatting) function for a column.
57779      * @param {Number} col The column index
57780      * @param {Function} fn The function to use to process the cell's raw data
57781      * to return HTML markup for the grid view. The render function is called with
57782      * the following parameters:<ul>
57783      * <li>Data value.</li>
57784      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57785      * <li>css A CSS style string to apply to the table cell.</li>
57786      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57787      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57788      * <li>Row index</li>
57789      * <li>Column index</li>
57790      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57791      */
57792     setRenderer : function(col, fn){
57793         this.config[col].renderer = fn;
57794     },
57795
57796     /**
57797      * Returns the width for the specified column.
57798      * @param {Number} col The column index
57799      * @return {Number}
57800      */
57801     getColumnWidth : function(col){
57802         return this.config[col].width * 1 || this.defaultWidth;
57803     },
57804
57805     /**
57806      * Sets the width for a column.
57807      * @param {Number} col The column index
57808      * @param {Number} width The new width
57809      */
57810     setColumnWidth : function(col, width, suppressEvent){
57811         this.config[col].width = width;
57812         this.totalWidth = null;
57813         if(!suppressEvent){
57814              this.fireEvent("widthchange", this, col, width);
57815         }
57816     },
57817
57818     /**
57819      * Returns the total width of all columns.
57820      * @param {Boolean} includeHidden True to include hidden column widths
57821      * @return {Number}
57822      */
57823     getTotalWidth : function(includeHidden){
57824         if(!this.totalWidth){
57825             this.totalWidth = 0;
57826             for(var i = 0, len = this.config.length; i < len; i++){
57827                 if(includeHidden || !this.isHidden(i)){
57828                     this.totalWidth += this.getColumnWidth(i);
57829                 }
57830             }
57831         }
57832         return this.totalWidth;
57833     },
57834
57835     /**
57836      * Returns the header for the specified column.
57837      * @param {Number} col The column index
57838      * @return {String}
57839      */
57840     getColumnHeader : function(col){
57841         return this.config[col].header;
57842     },
57843
57844     /**
57845      * Sets the header for a column.
57846      * @param {Number} col The column index
57847      * @param {String} header The new header
57848      */
57849     setColumnHeader : function(col, header){
57850         this.config[col].header = header;
57851         this.fireEvent("headerchange", this, col, header);
57852     },
57853
57854     /**
57855      * Returns the tooltip for the specified column.
57856      * @param {Number} col The column index
57857      * @return {String}
57858      */
57859     getColumnTooltip : function(col){
57860             return this.config[col].tooltip;
57861     },
57862     /**
57863      * Sets the tooltip for a column.
57864      * @param {Number} col The column index
57865      * @param {String} tooltip The new tooltip
57866      */
57867     setColumnTooltip : function(col, tooltip){
57868             this.config[col].tooltip = tooltip;
57869     },
57870
57871     /**
57872      * Returns the dataIndex for the specified column.
57873      * @param {Number} col The column index
57874      * @return {Number}
57875      */
57876     getDataIndex : function(col){
57877         return this.config[col].dataIndex;
57878     },
57879
57880     /**
57881      * Sets the dataIndex for a column.
57882      * @param {Number} col The column index
57883      * @param {Number} dataIndex The new dataIndex
57884      */
57885     setDataIndex : function(col, dataIndex){
57886         this.config[col].dataIndex = dataIndex;
57887     },
57888
57889     
57890     
57891     /**
57892      * Returns true if the cell is editable.
57893      * @param {Number} colIndex The column index
57894      * @param {Number} rowIndex The row index - this is nto actually used..?
57895      * @return {Boolean}
57896      */
57897     isCellEditable : function(colIndex, rowIndex){
57898         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57899     },
57900
57901     /**
57902      * Returns the editor defined for the cell/column.
57903      * return false or null to disable editing.
57904      * @param {Number} colIndex The column index
57905      * @param {Number} rowIndex The row index
57906      * @return {Object}
57907      */
57908     getCellEditor : function(colIndex, rowIndex){
57909         return this.config[colIndex].editor;
57910     },
57911
57912     /**
57913      * Sets if a column is editable.
57914      * @param {Number} col The column index
57915      * @param {Boolean} editable True if the column is editable
57916      */
57917     setEditable : function(col, editable){
57918         this.config[col].editable = editable;
57919     },
57920
57921
57922     /**
57923      * Returns true if the column is hidden.
57924      * @param {Number} colIndex The column index
57925      * @return {Boolean}
57926      */
57927     isHidden : function(colIndex){
57928         return this.config[colIndex].hidden;
57929     },
57930
57931
57932     /**
57933      * Returns true if the column width cannot be changed
57934      */
57935     isFixed : function(colIndex){
57936         return this.config[colIndex].fixed;
57937     },
57938
57939     /**
57940      * Returns true if the column can be resized
57941      * @return {Boolean}
57942      */
57943     isResizable : function(colIndex){
57944         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57945     },
57946     /**
57947      * Sets if a column is hidden.
57948      * @param {Number} colIndex The column index
57949      * @param {Boolean} hidden True if the column is hidden
57950      */
57951     setHidden : function(colIndex, hidden){
57952         this.config[colIndex].hidden = hidden;
57953         this.totalWidth = null;
57954         this.fireEvent("hiddenchange", this, colIndex, hidden);
57955     },
57956
57957     /**
57958      * Sets the editor for a column.
57959      * @param {Number} col The column index
57960      * @param {Object} editor The editor object
57961      */
57962     setEditor : function(col, editor){
57963         this.config[col].editor = editor;
57964     }
57965 });
57966
57967 Roo.grid.ColumnModel.defaultRenderer = function(value)
57968 {
57969     if(typeof value == "object") {
57970         return value;
57971     }
57972         if(typeof value == "string" && value.length < 1){
57973             return "&#160;";
57974         }
57975     
57976         return String.format("{0}", value);
57977 };
57978
57979 // Alias for backwards compatibility
57980 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57981 /*
57982  * Based on:
57983  * Ext JS Library 1.1.1
57984  * Copyright(c) 2006-2007, Ext JS, LLC.
57985  *
57986  * Originally Released Under LGPL - original licence link has changed is not relivant.
57987  *
57988  * Fork - LGPL
57989  * <script type="text/javascript">
57990  */
57991
57992 /**
57993  * @class Roo.grid.AbstractSelectionModel
57994  * @extends Roo.util.Observable
57995  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57996  * implemented by descendant classes.  This class should not be directly instantiated.
57997  * @constructor
57998  */
57999 Roo.grid.AbstractSelectionModel = function(){
58000     this.locked = false;
58001     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58002 };
58003
58004 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58005     /** @ignore Called by the grid automatically. Do not call directly. */
58006     init : function(grid){
58007         this.grid = grid;
58008         this.initEvents();
58009     },
58010
58011     /**
58012      * Locks the selections.
58013      */
58014     lock : function(){
58015         this.locked = true;
58016     },
58017
58018     /**
58019      * Unlocks the selections.
58020      */
58021     unlock : function(){
58022         this.locked = false;
58023     },
58024
58025     /**
58026      * Returns true if the selections are locked.
58027      * @return {Boolean}
58028      */
58029     isLocked : function(){
58030         return this.locked;
58031     }
58032 });/*
58033  * Based on:
58034  * Ext JS Library 1.1.1
58035  * Copyright(c) 2006-2007, Ext JS, LLC.
58036  *
58037  * Originally Released Under LGPL - original licence link has changed is not relivant.
58038  *
58039  * Fork - LGPL
58040  * <script type="text/javascript">
58041  */
58042 /**
58043  * @extends Roo.grid.AbstractSelectionModel
58044  * @class Roo.grid.RowSelectionModel
58045  * The default SelectionModel used by {@link Roo.grid.Grid}.
58046  * It supports multiple selections and keyboard selection/navigation. 
58047  * @constructor
58048  * @param {Object} config
58049  */
58050 Roo.grid.RowSelectionModel = function(config){
58051     Roo.apply(this, config);
58052     this.selections = new Roo.util.MixedCollection(false, function(o){
58053         return o.id;
58054     });
58055
58056     this.last = false;
58057     this.lastActive = false;
58058
58059     this.addEvents({
58060         /**
58061              * @event selectionchange
58062              * Fires when the selection changes
58063              * @param {SelectionModel} this
58064              */
58065             "selectionchange" : true,
58066         /**
58067              * @event afterselectionchange
58068              * Fires after the selection changes (eg. by key press or clicking)
58069              * @param {SelectionModel} this
58070              */
58071             "afterselectionchange" : true,
58072         /**
58073              * @event beforerowselect
58074              * Fires when a row is selected being selected, return false to cancel.
58075              * @param {SelectionModel} this
58076              * @param {Number} rowIndex The selected index
58077              * @param {Boolean} keepExisting False if other selections will be cleared
58078              */
58079             "beforerowselect" : true,
58080         /**
58081              * @event rowselect
58082              * Fires when a row is selected.
58083              * @param {SelectionModel} this
58084              * @param {Number} rowIndex The selected index
58085              * @param {Roo.data.Record} r The record
58086              */
58087             "rowselect" : true,
58088         /**
58089              * @event rowdeselect
58090              * Fires when a row is deselected.
58091              * @param {SelectionModel} this
58092              * @param {Number} rowIndex The selected index
58093              */
58094         "rowdeselect" : true
58095     });
58096     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58097     this.locked = false;
58098 };
58099
58100 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58101     /**
58102      * @cfg {Boolean} singleSelect
58103      * True to allow selection of only one row at a time (defaults to false)
58104      */
58105     singleSelect : false,
58106
58107     // private
58108     initEvents : function(){
58109
58110         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58111             this.grid.on("mousedown", this.handleMouseDown, this);
58112         }else{ // allow click to work like normal
58113             this.grid.on("rowclick", this.handleDragableRowClick, this);
58114         }
58115
58116         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58117             "up" : function(e){
58118                 if(!e.shiftKey){
58119                     this.selectPrevious(e.shiftKey);
58120                 }else if(this.last !== false && this.lastActive !== false){
58121                     var last = this.last;
58122                     this.selectRange(this.last,  this.lastActive-1);
58123                     this.grid.getView().focusRow(this.lastActive);
58124                     if(last !== false){
58125                         this.last = last;
58126                     }
58127                 }else{
58128                     this.selectFirstRow();
58129                 }
58130                 this.fireEvent("afterselectionchange", this);
58131             },
58132             "down" : function(e){
58133                 if(!e.shiftKey){
58134                     this.selectNext(e.shiftKey);
58135                 }else if(this.last !== false && this.lastActive !== false){
58136                     var last = this.last;
58137                     this.selectRange(this.last,  this.lastActive+1);
58138                     this.grid.getView().focusRow(this.lastActive);
58139                     if(last !== false){
58140                         this.last = last;
58141                     }
58142                 }else{
58143                     this.selectFirstRow();
58144                 }
58145                 this.fireEvent("afterselectionchange", this);
58146             },
58147             scope: this
58148         });
58149
58150         var view = this.grid.view;
58151         view.on("refresh", this.onRefresh, this);
58152         view.on("rowupdated", this.onRowUpdated, this);
58153         view.on("rowremoved", this.onRemove, this);
58154     },
58155
58156     // private
58157     onRefresh : function(){
58158         var ds = this.grid.dataSource, i, v = this.grid.view;
58159         var s = this.selections;
58160         s.each(function(r){
58161             if((i = ds.indexOfId(r.id)) != -1){
58162                 v.onRowSelect(i);
58163                 s.add(ds.getAt(i)); // updating the selection relate data
58164             }else{
58165                 s.remove(r);
58166             }
58167         });
58168     },
58169
58170     // private
58171     onRemove : function(v, index, r){
58172         this.selections.remove(r);
58173     },
58174
58175     // private
58176     onRowUpdated : function(v, index, r){
58177         if(this.isSelected(r)){
58178             v.onRowSelect(index);
58179         }
58180     },
58181
58182     /**
58183      * Select records.
58184      * @param {Array} records The records to select
58185      * @param {Boolean} keepExisting (optional) True to keep existing selections
58186      */
58187     selectRecords : function(records, keepExisting){
58188         if(!keepExisting){
58189             this.clearSelections();
58190         }
58191         var ds = this.grid.dataSource;
58192         for(var i = 0, len = records.length; i < len; i++){
58193             this.selectRow(ds.indexOf(records[i]), true);
58194         }
58195     },
58196
58197     /**
58198      * Gets the number of selected rows.
58199      * @return {Number}
58200      */
58201     getCount : function(){
58202         return this.selections.length;
58203     },
58204
58205     /**
58206      * Selects the first row in the grid.
58207      */
58208     selectFirstRow : function(){
58209         this.selectRow(0);
58210     },
58211
58212     /**
58213      * Select the last row.
58214      * @param {Boolean} keepExisting (optional) True to keep existing selections
58215      */
58216     selectLastRow : function(keepExisting){
58217         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58218     },
58219
58220     /**
58221      * Selects the row immediately following the last selected row.
58222      * @param {Boolean} keepExisting (optional) True to keep existing selections
58223      */
58224     selectNext : function(keepExisting){
58225         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58226             this.selectRow(this.last+1, keepExisting);
58227             this.grid.getView().focusRow(this.last);
58228         }
58229     },
58230
58231     /**
58232      * Selects the row that precedes the last selected row.
58233      * @param {Boolean} keepExisting (optional) True to keep existing selections
58234      */
58235     selectPrevious : function(keepExisting){
58236         if(this.last){
58237             this.selectRow(this.last-1, keepExisting);
58238             this.grid.getView().focusRow(this.last);
58239         }
58240     },
58241
58242     /**
58243      * Returns the selected records
58244      * @return {Array} Array of selected records
58245      */
58246     getSelections : function(){
58247         return [].concat(this.selections.items);
58248     },
58249
58250     /**
58251      * Returns the first selected record.
58252      * @return {Record}
58253      */
58254     getSelected : function(){
58255         return this.selections.itemAt(0);
58256     },
58257
58258
58259     /**
58260      * Clears all selections.
58261      */
58262     clearSelections : function(fast){
58263         if(this.locked) {
58264             return;
58265         }
58266         if(fast !== true){
58267             var ds = this.grid.dataSource;
58268             var s = this.selections;
58269             s.each(function(r){
58270                 this.deselectRow(ds.indexOfId(r.id));
58271             }, this);
58272             s.clear();
58273         }else{
58274             this.selections.clear();
58275         }
58276         this.last = false;
58277     },
58278
58279
58280     /**
58281      * Selects all rows.
58282      */
58283     selectAll : function(){
58284         if(this.locked) {
58285             return;
58286         }
58287         this.selections.clear();
58288         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58289             this.selectRow(i, true);
58290         }
58291     },
58292
58293     /**
58294      * Returns True if there is a selection.
58295      * @return {Boolean}
58296      */
58297     hasSelection : function(){
58298         return this.selections.length > 0;
58299     },
58300
58301     /**
58302      * Returns True if the specified row is selected.
58303      * @param {Number/Record} record The record or index of the record to check
58304      * @return {Boolean}
58305      */
58306     isSelected : function(index){
58307         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58308         return (r && this.selections.key(r.id) ? true : false);
58309     },
58310
58311     /**
58312      * Returns True if the specified record id is selected.
58313      * @param {String} id The id of record to check
58314      * @return {Boolean}
58315      */
58316     isIdSelected : function(id){
58317         return (this.selections.key(id) ? true : false);
58318     },
58319
58320     // private
58321     handleMouseDown : function(e, t){
58322         var view = this.grid.getView(), rowIndex;
58323         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58324             return;
58325         };
58326         if(e.shiftKey && this.last !== false){
58327             var last = this.last;
58328             this.selectRange(last, rowIndex, e.ctrlKey);
58329             this.last = last; // reset the last
58330             view.focusRow(rowIndex);
58331         }else{
58332             var isSelected = this.isSelected(rowIndex);
58333             if(e.button !== 0 && isSelected){
58334                 view.focusRow(rowIndex);
58335             }else if(e.ctrlKey && isSelected){
58336                 this.deselectRow(rowIndex);
58337             }else if(!isSelected){
58338                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58339                 view.focusRow(rowIndex);
58340             }
58341         }
58342         this.fireEvent("afterselectionchange", this);
58343     },
58344     // private
58345     handleDragableRowClick :  function(grid, rowIndex, e) 
58346     {
58347         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58348             this.selectRow(rowIndex, false);
58349             grid.view.focusRow(rowIndex);
58350              this.fireEvent("afterselectionchange", this);
58351         }
58352     },
58353     
58354     /**
58355      * Selects multiple rows.
58356      * @param {Array} rows Array of the indexes of the row to select
58357      * @param {Boolean} keepExisting (optional) True to keep existing selections
58358      */
58359     selectRows : function(rows, keepExisting){
58360         if(!keepExisting){
58361             this.clearSelections();
58362         }
58363         for(var i = 0, len = rows.length; i < len; i++){
58364             this.selectRow(rows[i], true);
58365         }
58366     },
58367
58368     /**
58369      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58370      * @param {Number} startRow The index of the first row in the range
58371      * @param {Number} endRow The index of the last row in the range
58372      * @param {Boolean} keepExisting (optional) True to retain existing selections
58373      */
58374     selectRange : function(startRow, endRow, keepExisting){
58375         if(this.locked) {
58376             return;
58377         }
58378         if(!keepExisting){
58379             this.clearSelections();
58380         }
58381         if(startRow <= endRow){
58382             for(var i = startRow; i <= endRow; i++){
58383                 this.selectRow(i, true);
58384             }
58385         }else{
58386             for(var i = startRow; i >= endRow; i--){
58387                 this.selectRow(i, true);
58388             }
58389         }
58390     },
58391
58392     /**
58393      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58394      * @param {Number} startRow The index of the first row in the range
58395      * @param {Number} endRow The index of the last row in the range
58396      */
58397     deselectRange : function(startRow, endRow, preventViewNotify){
58398         if(this.locked) {
58399             return;
58400         }
58401         for(var i = startRow; i <= endRow; i++){
58402             this.deselectRow(i, preventViewNotify);
58403         }
58404     },
58405
58406     /**
58407      * Selects a row.
58408      * @param {Number} row The index of the row to select
58409      * @param {Boolean} keepExisting (optional) True to keep existing selections
58410      */
58411     selectRow : function(index, keepExisting, preventViewNotify){
58412         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58413             return;
58414         }
58415         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58416             if(!keepExisting || this.singleSelect){
58417                 this.clearSelections();
58418             }
58419             var r = this.grid.dataSource.getAt(index);
58420             this.selections.add(r);
58421             this.last = this.lastActive = index;
58422             if(!preventViewNotify){
58423                 this.grid.getView().onRowSelect(index);
58424             }
58425             this.fireEvent("rowselect", this, index, r);
58426             this.fireEvent("selectionchange", this);
58427         }
58428     },
58429
58430     /**
58431      * Deselects a row.
58432      * @param {Number} row The index of the row to deselect
58433      */
58434     deselectRow : function(index, preventViewNotify){
58435         if(this.locked) {
58436             return;
58437         }
58438         if(this.last == index){
58439             this.last = false;
58440         }
58441         if(this.lastActive == index){
58442             this.lastActive = false;
58443         }
58444         var r = this.grid.dataSource.getAt(index);
58445         this.selections.remove(r);
58446         if(!preventViewNotify){
58447             this.grid.getView().onRowDeselect(index);
58448         }
58449         this.fireEvent("rowdeselect", this, index);
58450         this.fireEvent("selectionchange", this);
58451     },
58452
58453     // private
58454     restoreLast : function(){
58455         if(this._last){
58456             this.last = this._last;
58457         }
58458     },
58459
58460     // private
58461     acceptsNav : function(row, col, cm){
58462         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58463     },
58464
58465     // private
58466     onEditorKey : function(field, e){
58467         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58468         if(k == e.TAB){
58469             e.stopEvent();
58470             ed.completeEdit();
58471             if(e.shiftKey){
58472                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58473             }else{
58474                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58475             }
58476         }else if(k == e.ENTER && !e.ctrlKey){
58477             e.stopEvent();
58478             ed.completeEdit();
58479             if(e.shiftKey){
58480                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58481             }else{
58482                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58483             }
58484         }else if(k == e.ESC){
58485             ed.cancelEdit();
58486         }
58487         if(newCell){
58488             g.startEditing(newCell[0], newCell[1]);
58489         }
58490     }
58491 });/*
58492  * Based on:
58493  * Ext JS Library 1.1.1
58494  * Copyright(c) 2006-2007, Ext JS, LLC.
58495  *
58496  * Originally Released Under LGPL - original licence link has changed is not relivant.
58497  *
58498  * Fork - LGPL
58499  * <script type="text/javascript">
58500  */
58501 /**
58502  * @class Roo.grid.CellSelectionModel
58503  * @extends Roo.grid.AbstractSelectionModel
58504  * This class provides the basic implementation for cell selection in a grid.
58505  * @constructor
58506  * @param {Object} config The object containing the configuration of this model.
58507  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58508  */
58509 Roo.grid.CellSelectionModel = function(config){
58510     Roo.apply(this, config);
58511
58512     this.selection = null;
58513
58514     this.addEvents({
58515         /**
58516              * @event beforerowselect
58517              * Fires before a cell is selected.
58518              * @param {SelectionModel} this
58519              * @param {Number} rowIndex The selected row index
58520              * @param {Number} colIndex The selected cell index
58521              */
58522             "beforecellselect" : true,
58523         /**
58524              * @event cellselect
58525              * Fires when a cell is selected.
58526              * @param {SelectionModel} this
58527              * @param {Number} rowIndex The selected row index
58528              * @param {Number} colIndex The selected cell index
58529              */
58530             "cellselect" : true,
58531         /**
58532              * @event selectionchange
58533              * Fires when the active selection changes.
58534              * @param {SelectionModel} this
58535              * @param {Object} selection null for no selection or an object (o) with two properties
58536                 <ul>
58537                 <li>o.record: the record object for the row the selection is in</li>
58538                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58539                 </ul>
58540              */
58541             "selectionchange" : true,
58542         /**
58543              * @event tabend
58544              * Fires when the tab (or enter) was pressed on the last editable cell
58545              * You can use this to trigger add new row.
58546              * @param {SelectionModel} this
58547              */
58548             "tabend" : true,
58549          /**
58550              * @event beforeeditnext
58551              * Fires before the next editable sell is made active
58552              * You can use this to skip to another cell or fire the tabend
58553              *    if you set cell to false
58554              * @param {Object} eventdata object : { cell : [ row, col ] } 
58555              */
58556             "beforeeditnext" : true
58557     });
58558     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58559 };
58560
58561 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58562     
58563     enter_is_tab: false,
58564
58565     /** @ignore */
58566     initEvents : function(){
58567         this.grid.on("mousedown", this.handleMouseDown, this);
58568         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58569         var view = this.grid.view;
58570         view.on("refresh", this.onViewChange, this);
58571         view.on("rowupdated", this.onRowUpdated, this);
58572         view.on("beforerowremoved", this.clearSelections, this);
58573         view.on("beforerowsinserted", this.clearSelections, this);
58574         if(this.grid.isEditor){
58575             this.grid.on("beforeedit", this.beforeEdit,  this);
58576         }
58577     },
58578
58579         //private
58580     beforeEdit : function(e){
58581         this.select(e.row, e.column, false, true, e.record);
58582     },
58583
58584         //private
58585     onRowUpdated : function(v, index, r){
58586         if(this.selection && this.selection.record == r){
58587             v.onCellSelect(index, this.selection.cell[1]);
58588         }
58589     },
58590
58591         //private
58592     onViewChange : function(){
58593         this.clearSelections(true);
58594     },
58595
58596         /**
58597          * Returns the currently selected cell,.
58598          * @return {Array} The selected cell (row, column) or null if none selected.
58599          */
58600     getSelectedCell : function(){
58601         return this.selection ? this.selection.cell : null;
58602     },
58603
58604     /**
58605      * Clears all selections.
58606      * @param {Boolean} true to prevent the gridview from being notified about the change.
58607      */
58608     clearSelections : function(preventNotify){
58609         var s = this.selection;
58610         if(s){
58611             if(preventNotify !== true){
58612                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58613             }
58614             this.selection = null;
58615             this.fireEvent("selectionchange", this, null);
58616         }
58617     },
58618
58619     /**
58620      * Returns true if there is a selection.
58621      * @return {Boolean}
58622      */
58623     hasSelection : function(){
58624         return this.selection ? true : false;
58625     },
58626
58627     /** @ignore */
58628     handleMouseDown : function(e, t){
58629         var v = this.grid.getView();
58630         if(this.isLocked()){
58631             return;
58632         };
58633         var row = v.findRowIndex(t);
58634         var cell = v.findCellIndex(t);
58635         if(row !== false && cell !== false){
58636             this.select(row, cell);
58637         }
58638     },
58639
58640     /**
58641      * Selects a cell.
58642      * @param {Number} rowIndex
58643      * @param {Number} collIndex
58644      */
58645     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58646         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58647             this.clearSelections();
58648             r = r || this.grid.dataSource.getAt(rowIndex);
58649             this.selection = {
58650                 record : r,
58651                 cell : [rowIndex, colIndex]
58652             };
58653             if(!preventViewNotify){
58654                 var v = this.grid.getView();
58655                 v.onCellSelect(rowIndex, colIndex);
58656                 if(preventFocus !== true){
58657                     v.focusCell(rowIndex, colIndex);
58658                 }
58659             }
58660             this.fireEvent("cellselect", this, rowIndex, colIndex);
58661             this.fireEvent("selectionchange", this, this.selection);
58662         }
58663     },
58664
58665         //private
58666     isSelectable : function(rowIndex, colIndex, cm){
58667         return !cm.isHidden(colIndex);
58668     },
58669
58670     /** @ignore */
58671     handleKeyDown : function(e){
58672         //Roo.log('Cell Sel Model handleKeyDown');
58673         if(!e.isNavKeyPress()){
58674             return;
58675         }
58676         var g = this.grid, s = this.selection;
58677         if(!s){
58678             e.stopEvent();
58679             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58680             if(cell){
58681                 this.select(cell[0], cell[1]);
58682             }
58683             return;
58684         }
58685         var sm = this;
58686         var walk = function(row, col, step){
58687             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58688         };
58689         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58690         var newCell;
58691
58692       
58693
58694         switch(k){
58695             case e.TAB:
58696                 // handled by onEditorKey
58697                 if (g.isEditor && g.editing) {
58698                     return;
58699                 }
58700                 if(e.shiftKey) {
58701                     newCell = walk(r, c-1, -1);
58702                 } else {
58703                     newCell = walk(r, c+1, 1);
58704                 }
58705                 break;
58706             
58707             case e.DOWN:
58708                newCell = walk(r+1, c, 1);
58709                 break;
58710             
58711             case e.UP:
58712                 newCell = walk(r-1, c, -1);
58713                 break;
58714             
58715             case e.RIGHT:
58716                 newCell = walk(r, c+1, 1);
58717                 break;
58718             
58719             case e.LEFT:
58720                 newCell = walk(r, c-1, -1);
58721                 break;
58722             
58723             case e.ENTER:
58724                 
58725                 if(g.isEditor && !g.editing){
58726                    g.startEditing(r, c);
58727                    e.stopEvent();
58728                    return;
58729                 }
58730                 
58731                 
58732              break;
58733         };
58734         if(newCell){
58735             this.select(newCell[0], newCell[1]);
58736             e.stopEvent();
58737             
58738         }
58739     },
58740
58741     acceptsNav : function(row, col, cm){
58742         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58743     },
58744     /**
58745      * Selects a cell.
58746      * @param {Number} field (not used) - as it's normally used as a listener
58747      * @param {Number} e - event - fake it by using
58748      *
58749      * var e = Roo.EventObjectImpl.prototype;
58750      * e.keyCode = e.TAB
58751      *
58752      * 
58753      */
58754     onEditorKey : function(field, e){
58755         
58756         var k = e.getKey(),
58757             newCell,
58758             g = this.grid,
58759             ed = g.activeEditor,
58760             forward = false;
58761         ///Roo.log('onEditorKey' + k);
58762         
58763         
58764         if (this.enter_is_tab && k == e.ENTER) {
58765             k = e.TAB;
58766         }
58767         
58768         if(k == e.TAB){
58769             if(e.shiftKey){
58770                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58771             }else{
58772                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58773                 forward = true;
58774             }
58775             
58776             e.stopEvent();
58777             
58778         } else if(k == e.ENTER &&  !e.ctrlKey){
58779             ed.completeEdit();
58780             e.stopEvent();
58781             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58782         
58783                 } else if(k == e.ESC){
58784             ed.cancelEdit();
58785         }
58786                 
58787         if (newCell) {
58788             var ecall = { cell : newCell, forward : forward };
58789             this.fireEvent('beforeeditnext', ecall );
58790             newCell = ecall.cell;
58791                         forward = ecall.forward;
58792         }
58793                 
58794         if(newCell){
58795             //Roo.log('next cell after edit');
58796             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58797         } else if (forward) {
58798             // tabbed past last
58799             this.fireEvent.defer(100, this, ['tabend',this]);
58800         }
58801     }
58802 });/*
58803  * Based on:
58804  * Ext JS Library 1.1.1
58805  * Copyright(c) 2006-2007, Ext JS, LLC.
58806  *
58807  * Originally Released Under LGPL - original licence link has changed is not relivant.
58808  *
58809  * Fork - LGPL
58810  * <script type="text/javascript">
58811  */
58812  
58813 /**
58814  * @class Roo.grid.EditorGrid
58815  * @extends Roo.grid.Grid
58816  * Class for creating and editable grid.
58817  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58818  * The container MUST have some type of size defined for the grid to fill. The container will be 
58819  * automatically set to position relative if it isn't already.
58820  * @param {Object} dataSource The data model to bind to
58821  * @param {Object} colModel The column model with info about this grid's columns
58822  */
58823 Roo.grid.EditorGrid = function(container, config){
58824     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58825     this.getGridEl().addClass("xedit-grid");
58826
58827     if(!this.selModel){
58828         this.selModel = new Roo.grid.CellSelectionModel();
58829     }
58830
58831     this.activeEditor = null;
58832
58833         this.addEvents({
58834             /**
58835              * @event beforeedit
58836              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58837              * <ul style="padding:5px;padding-left:16px;">
58838              * <li>grid - This grid</li>
58839              * <li>record - The record being edited</li>
58840              * <li>field - The field name being edited</li>
58841              * <li>value - The value for the field being edited.</li>
58842              * <li>row - The grid row index</li>
58843              * <li>column - The grid column index</li>
58844              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58845              * </ul>
58846              * @param {Object} e An edit event (see above for description)
58847              */
58848             "beforeedit" : true,
58849             /**
58850              * @event afteredit
58851              * Fires after a cell is edited. <br />
58852              * <ul style="padding:5px;padding-left:16px;">
58853              * <li>grid - This grid</li>
58854              * <li>record - The record being edited</li>
58855              * <li>field - The field name being edited</li>
58856              * <li>value - The value being set</li>
58857              * <li>originalValue - The original value for the field, before the edit.</li>
58858              * <li>row - The grid row index</li>
58859              * <li>column - The grid column index</li>
58860              * </ul>
58861              * @param {Object} e An edit event (see above for description)
58862              */
58863             "afteredit" : true,
58864             /**
58865              * @event validateedit
58866              * Fires after a cell is edited, but before the value is set in the record. 
58867          * You can use this to modify the value being set in the field, Return false
58868              * to cancel the change. The edit event object has the following properties <br />
58869              * <ul style="padding:5px;padding-left:16px;">
58870          * <li>editor - This editor</li>
58871              * <li>grid - This grid</li>
58872              * <li>record - The record being edited</li>
58873              * <li>field - The field name being edited</li>
58874              * <li>value - The value being set</li>
58875              * <li>originalValue - The original value for the field, before the edit.</li>
58876              * <li>row - The grid row index</li>
58877              * <li>column - The grid column index</li>
58878              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58879              * </ul>
58880              * @param {Object} e An edit event (see above for description)
58881              */
58882             "validateedit" : true
58883         });
58884     this.on("bodyscroll", this.stopEditing,  this);
58885     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58886 };
58887
58888 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58889     /**
58890      * @cfg {Number} clicksToEdit
58891      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58892      */
58893     clicksToEdit: 2,
58894
58895     // private
58896     isEditor : true,
58897     // private
58898     trackMouseOver: false, // causes very odd FF errors
58899
58900     onCellDblClick : function(g, row, col){
58901         this.startEditing(row, col);
58902     },
58903
58904     onEditComplete : function(ed, value, startValue){
58905         this.editing = false;
58906         this.activeEditor = null;
58907         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58908         var r = ed.record;
58909         var field = this.colModel.getDataIndex(ed.col);
58910         var e = {
58911             grid: this,
58912             record: r,
58913             field: field,
58914             originalValue: startValue,
58915             value: value,
58916             row: ed.row,
58917             column: ed.col,
58918             cancel:false,
58919             editor: ed
58920         };
58921         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58922         cell.show();
58923           
58924         if(String(value) !== String(startValue)){
58925             
58926             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58927                 r.set(field, e.value);
58928                 // if we are dealing with a combo box..
58929                 // then we also set the 'name' colum to be the displayField
58930                 if (ed.field.displayField && ed.field.name) {
58931                     r.set(ed.field.name, ed.field.el.dom.value);
58932                 }
58933                 
58934                 delete e.cancel; //?? why!!!
58935                 this.fireEvent("afteredit", e);
58936             }
58937         } else {
58938             this.fireEvent("afteredit", e); // always fire it!
58939         }
58940         this.view.focusCell(ed.row, ed.col);
58941     },
58942
58943     /**
58944      * Starts editing the specified for the specified row/column
58945      * @param {Number} rowIndex
58946      * @param {Number} colIndex
58947      */
58948     startEditing : function(row, col){
58949         this.stopEditing();
58950         if(this.colModel.isCellEditable(col, row)){
58951             this.view.ensureVisible(row, col, true);
58952           
58953             var r = this.dataSource.getAt(row);
58954             var field = this.colModel.getDataIndex(col);
58955             var cell = Roo.get(this.view.getCell(row,col));
58956             var e = {
58957                 grid: this,
58958                 record: r,
58959                 field: field,
58960                 value: r.data[field],
58961                 row: row,
58962                 column: col,
58963                 cancel:false 
58964             };
58965             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58966                 this.editing = true;
58967                 var ed = this.colModel.getCellEditor(col, row);
58968                 
58969                 if (!ed) {
58970                     return;
58971                 }
58972                 if(!ed.rendered){
58973                     ed.render(ed.parentEl || document.body);
58974                 }
58975                 ed.field.reset();
58976                
58977                 cell.hide();
58978                 
58979                 (function(){ // complex but required for focus issues in safari, ie and opera
58980                     ed.row = row;
58981                     ed.col = col;
58982                     ed.record = r;
58983                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58984                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58985                     this.activeEditor = ed;
58986                     var v = r.data[field];
58987                     ed.startEdit(this.view.getCell(row, col), v);
58988                     // combo's with 'displayField and name set
58989                     if (ed.field.displayField && ed.field.name) {
58990                         ed.field.el.dom.value = r.data[ed.field.name];
58991                     }
58992                     
58993                     
58994                 }).defer(50, this);
58995             }
58996         }
58997     },
58998         
58999     /**
59000      * Stops any active editing
59001      */
59002     stopEditing : function(){
59003         if(this.activeEditor){
59004             this.activeEditor.completeEdit();
59005         }
59006         this.activeEditor = null;
59007     },
59008         
59009          /**
59010      * Called to get grid's drag proxy text, by default returns this.ddText.
59011      * @return {String}
59012      */
59013     getDragDropText : function(){
59014         var count = this.selModel.getSelectedCell() ? 1 : 0;
59015         return String.format(this.ddText, count, count == 1 ? '' : 's');
59016     }
59017         
59018 });/*
59019  * Based on:
59020  * Ext JS Library 1.1.1
59021  * Copyright(c) 2006-2007, Ext JS, LLC.
59022  *
59023  * Originally Released Under LGPL - original licence link has changed is not relivant.
59024  *
59025  * Fork - LGPL
59026  * <script type="text/javascript">
59027  */
59028
59029 // private - not really -- you end up using it !
59030 // This is a support class used internally by the Grid components
59031
59032 /**
59033  * @class Roo.grid.GridEditor
59034  * @extends Roo.Editor
59035  * Class for creating and editable grid elements.
59036  * @param {Object} config any settings (must include field)
59037  */
59038 Roo.grid.GridEditor = function(field, config){
59039     if (!config && field.field) {
59040         config = field;
59041         field = Roo.factory(config.field, Roo.form);
59042     }
59043     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59044     field.monitorTab = false;
59045 };
59046
59047 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59048     
59049     /**
59050      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59051      */
59052     
59053     alignment: "tl-tl",
59054     autoSize: "width",
59055     hideEl : false,
59056     cls: "x-small-editor x-grid-editor",
59057     shim:false,
59058     shadow:"frame"
59059 });/*
59060  * Based on:
59061  * Ext JS Library 1.1.1
59062  * Copyright(c) 2006-2007, Ext JS, LLC.
59063  *
59064  * Originally Released Under LGPL - original licence link has changed is not relivant.
59065  *
59066  * Fork - LGPL
59067  * <script type="text/javascript">
59068  */
59069   
59070
59071   
59072 Roo.grid.PropertyRecord = Roo.data.Record.create([
59073     {name:'name',type:'string'},  'value'
59074 ]);
59075
59076
59077 Roo.grid.PropertyStore = function(grid, source){
59078     this.grid = grid;
59079     this.store = new Roo.data.Store({
59080         recordType : Roo.grid.PropertyRecord
59081     });
59082     this.store.on('update', this.onUpdate,  this);
59083     if(source){
59084         this.setSource(source);
59085     }
59086     Roo.grid.PropertyStore.superclass.constructor.call(this);
59087 };
59088
59089
59090
59091 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59092     setSource : function(o){
59093         this.source = o;
59094         this.store.removeAll();
59095         var data = [];
59096         for(var k in o){
59097             if(this.isEditableValue(o[k])){
59098                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59099             }
59100         }
59101         this.store.loadRecords({records: data}, {}, true);
59102     },
59103
59104     onUpdate : function(ds, record, type){
59105         if(type == Roo.data.Record.EDIT){
59106             var v = record.data['value'];
59107             var oldValue = record.modified['value'];
59108             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59109                 this.source[record.id] = v;
59110                 record.commit();
59111                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59112             }else{
59113                 record.reject();
59114             }
59115         }
59116     },
59117
59118     getProperty : function(row){
59119        return this.store.getAt(row);
59120     },
59121
59122     isEditableValue: function(val){
59123         if(val && val instanceof Date){
59124             return true;
59125         }else if(typeof val == 'object' || typeof val == 'function'){
59126             return false;
59127         }
59128         return true;
59129     },
59130
59131     setValue : function(prop, value){
59132         this.source[prop] = value;
59133         this.store.getById(prop).set('value', value);
59134     },
59135
59136     getSource : function(){
59137         return this.source;
59138     }
59139 });
59140
59141 Roo.grid.PropertyColumnModel = function(grid, store){
59142     this.grid = grid;
59143     var g = Roo.grid;
59144     g.PropertyColumnModel.superclass.constructor.call(this, [
59145         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59146         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59147     ]);
59148     this.store = store;
59149     this.bselect = Roo.DomHelper.append(document.body, {
59150         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59151             {tag: 'option', value: 'true', html: 'true'},
59152             {tag: 'option', value: 'false', html: 'false'}
59153         ]
59154     });
59155     Roo.id(this.bselect);
59156     var f = Roo.form;
59157     this.editors = {
59158         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59159         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59160         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59161         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59162         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59163     };
59164     this.renderCellDelegate = this.renderCell.createDelegate(this);
59165     this.renderPropDelegate = this.renderProp.createDelegate(this);
59166 };
59167
59168 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59169     
59170     
59171     nameText : 'Name',
59172     valueText : 'Value',
59173     
59174     dateFormat : 'm/j/Y',
59175     
59176     
59177     renderDate : function(dateVal){
59178         return dateVal.dateFormat(this.dateFormat);
59179     },
59180
59181     renderBool : function(bVal){
59182         return bVal ? 'true' : 'false';
59183     },
59184
59185     isCellEditable : function(colIndex, rowIndex){
59186         return colIndex == 1;
59187     },
59188
59189     getRenderer : function(col){
59190         return col == 1 ?
59191             this.renderCellDelegate : this.renderPropDelegate;
59192     },
59193
59194     renderProp : function(v){
59195         return this.getPropertyName(v);
59196     },
59197
59198     renderCell : function(val){
59199         var rv = val;
59200         if(val instanceof Date){
59201             rv = this.renderDate(val);
59202         }else if(typeof val == 'boolean'){
59203             rv = this.renderBool(val);
59204         }
59205         return Roo.util.Format.htmlEncode(rv);
59206     },
59207
59208     getPropertyName : function(name){
59209         var pn = this.grid.propertyNames;
59210         return pn && pn[name] ? pn[name] : name;
59211     },
59212
59213     getCellEditor : function(colIndex, rowIndex){
59214         var p = this.store.getProperty(rowIndex);
59215         var n = p.data['name'], val = p.data['value'];
59216         
59217         if(typeof(this.grid.customEditors[n]) == 'string'){
59218             return this.editors[this.grid.customEditors[n]];
59219         }
59220         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59221             return this.grid.customEditors[n];
59222         }
59223         if(val instanceof Date){
59224             return this.editors['date'];
59225         }else if(typeof val == 'number'){
59226             return this.editors['number'];
59227         }else if(typeof val == 'boolean'){
59228             return this.editors['boolean'];
59229         }else{
59230             return this.editors['string'];
59231         }
59232     }
59233 });
59234
59235 /**
59236  * @class Roo.grid.PropertyGrid
59237  * @extends Roo.grid.EditorGrid
59238  * This class represents the  interface of a component based property grid control.
59239  * <br><br>Usage:<pre><code>
59240  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59241       
59242  });
59243  // set any options
59244  grid.render();
59245  * </code></pre>
59246   
59247  * @constructor
59248  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59249  * The container MUST have some type of size defined for the grid to fill. The container will be
59250  * automatically set to position relative if it isn't already.
59251  * @param {Object} config A config object that sets properties on this grid.
59252  */
59253 Roo.grid.PropertyGrid = function(container, config){
59254     config = config || {};
59255     var store = new Roo.grid.PropertyStore(this);
59256     this.store = store;
59257     var cm = new Roo.grid.PropertyColumnModel(this, store);
59258     store.store.sort('name', 'ASC');
59259     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59260         ds: store.store,
59261         cm: cm,
59262         enableColLock:false,
59263         enableColumnMove:false,
59264         stripeRows:false,
59265         trackMouseOver: false,
59266         clicksToEdit:1
59267     }, config));
59268     this.getGridEl().addClass('x-props-grid');
59269     this.lastEditRow = null;
59270     this.on('columnresize', this.onColumnResize, this);
59271     this.addEvents({
59272          /**
59273              * @event beforepropertychange
59274              * Fires before a property changes (return false to stop?)
59275              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59276              * @param {String} id Record Id
59277              * @param {String} newval New Value
59278          * @param {String} oldval Old Value
59279              */
59280         "beforepropertychange": true,
59281         /**
59282              * @event propertychange
59283              * Fires after a property changes
59284              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59285              * @param {String} id Record Id
59286              * @param {String} newval New Value
59287          * @param {String} oldval Old Value
59288              */
59289         "propertychange": true
59290     });
59291     this.customEditors = this.customEditors || {};
59292 };
59293 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59294     
59295      /**
59296      * @cfg {Object} customEditors map of colnames=> custom editors.
59297      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59298      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59299      * false disables editing of the field.
59300          */
59301     
59302       /**
59303      * @cfg {Object} propertyNames map of property Names to their displayed value
59304          */
59305     
59306     render : function(){
59307         Roo.grid.PropertyGrid.superclass.render.call(this);
59308         this.autoSize.defer(100, this);
59309     },
59310
59311     autoSize : function(){
59312         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59313         if(this.view){
59314             this.view.fitColumns();
59315         }
59316     },
59317
59318     onColumnResize : function(){
59319         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59320         this.autoSize();
59321     },
59322     /**
59323      * Sets the data for the Grid
59324      * accepts a Key => Value object of all the elements avaiable.
59325      * @param {Object} data  to appear in grid.
59326      */
59327     setSource : function(source){
59328         this.store.setSource(source);
59329         //this.autoSize();
59330     },
59331     /**
59332      * Gets all the data from the grid.
59333      * @return {Object} data  data stored in grid
59334      */
59335     getSource : function(){
59336         return this.store.getSource();
59337     }
59338 });/*
59339   
59340  * Licence LGPL
59341  
59342  */
59343  
59344 /**
59345  * @class Roo.grid.Calendar
59346  * @extends Roo.util.Grid
59347  * This class extends the Grid to provide a calendar widget
59348  * <br><br>Usage:<pre><code>
59349  var grid = new Roo.grid.Calendar("my-container-id", {
59350      ds: myDataStore,
59351      cm: myColModel,
59352      selModel: mySelectionModel,
59353      autoSizeColumns: true,
59354      monitorWindowResize: false,
59355      trackMouseOver: true
59356      eventstore : real data store..
59357  });
59358  // set any options
59359  grid.render();
59360   
59361   * @constructor
59362  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59363  * The container MUST have some type of size defined for the grid to fill. The container will be
59364  * automatically set to position relative if it isn't already.
59365  * @param {Object} config A config object that sets properties on this grid.
59366  */
59367 Roo.grid.Calendar = function(container, config){
59368         // initialize the container
59369         this.container = Roo.get(container);
59370         this.container.update("");
59371         this.container.setStyle("overflow", "hidden");
59372     this.container.addClass('x-grid-container');
59373
59374     this.id = this.container.id;
59375
59376     Roo.apply(this, config);
59377     // check and correct shorthanded configs
59378     
59379     var rows = [];
59380     var d =1;
59381     for (var r = 0;r < 6;r++) {
59382         
59383         rows[r]=[];
59384         for (var c =0;c < 7;c++) {
59385             rows[r][c]= '';
59386         }
59387     }
59388     if (this.eventStore) {
59389         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59390         this.eventStore.on('load',this.onLoad, this);
59391         this.eventStore.on('beforeload',this.clearEvents, this);
59392          
59393     }
59394     
59395     this.dataSource = new Roo.data.Store({
59396             proxy: new Roo.data.MemoryProxy(rows),
59397             reader: new Roo.data.ArrayReader({}, [
59398                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59399     });
59400
59401     this.dataSource.load();
59402     this.ds = this.dataSource;
59403     this.ds.xmodule = this.xmodule || false;
59404     
59405     
59406     var cellRender = function(v,x,r)
59407     {
59408         return String.format(
59409             '<div class="fc-day  fc-widget-content"><div>' +
59410                 '<div class="fc-event-container"></div>' +
59411                 '<div class="fc-day-number">{0}</div>'+
59412                 
59413                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59414             '</div></div>', v);
59415     
59416     }
59417     
59418     
59419     this.colModel = new Roo.grid.ColumnModel( [
59420         {
59421             xtype: 'ColumnModel',
59422             xns: Roo.grid,
59423             dataIndex : 'weekday0',
59424             header : 'Sunday',
59425             renderer : cellRender
59426         },
59427         {
59428             xtype: 'ColumnModel',
59429             xns: Roo.grid,
59430             dataIndex : 'weekday1',
59431             header : 'Monday',
59432             renderer : cellRender
59433         },
59434         {
59435             xtype: 'ColumnModel',
59436             xns: Roo.grid,
59437             dataIndex : 'weekday2',
59438             header : 'Tuesday',
59439             renderer : cellRender
59440         },
59441         {
59442             xtype: 'ColumnModel',
59443             xns: Roo.grid,
59444             dataIndex : 'weekday3',
59445             header : 'Wednesday',
59446             renderer : cellRender
59447         },
59448         {
59449             xtype: 'ColumnModel',
59450             xns: Roo.grid,
59451             dataIndex : 'weekday4',
59452             header : 'Thursday',
59453             renderer : cellRender
59454         },
59455         {
59456             xtype: 'ColumnModel',
59457             xns: Roo.grid,
59458             dataIndex : 'weekday5',
59459             header : 'Friday',
59460             renderer : cellRender
59461         },
59462         {
59463             xtype: 'ColumnModel',
59464             xns: Roo.grid,
59465             dataIndex : 'weekday6',
59466             header : 'Saturday',
59467             renderer : cellRender
59468         }
59469     ]);
59470     this.cm = this.colModel;
59471     this.cm.xmodule = this.xmodule || false;
59472  
59473         
59474           
59475     //this.selModel = new Roo.grid.CellSelectionModel();
59476     //this.sm = this.selModel;
59477     //this.selModel.init(this);
59478     
59479     
59480     if(this.width){
59481         this.container.setWidth(this.width);
59482     }
59483
59484     if(this.height){
59485         this.container.setHeight(this.height);
59486     }
59487     /** @private */
59488         this.addEvents({
59489         // raw events
59490         /**
59491          * @event click
59492          * The raw click event for the entire grid.
59493          * @param {Roo.EventObject} e
59494          */
59495         "click" : true,
59496         /**
59497          * @event dblclick
59498          * The raw dblclick event for the entire grid.
59499          * @param {Roo.EventObject} e
59500          */
59501         "dblclick" : true,
59502         /**
59503          * @event contextmenu
59504          * The raw contextmenu event for the entire grid.
59505          * @param {Roo.EventObject} e
59506          */
59507         "contextmenu" : true,
59508         /**
59509          * @event mousedown
59510          * The raw mousedown event for the entire grid.
59511          * @param {Roo.EventObject} e
59512          */
59513         "mousedown" : true,
59514         /**
59515          * @event mouseup
59516          * The raw mouseup event for the entire grid.
59517          * @param {Roo.EventObject} e
59518          */
59519         "mouseup" : true,
59520         /**
59521          * @event mouseover
59522          * The raw mouseover event for the entire grid.
59523          * @param {Roo.EventObject} e
59524          */
59525         "mouseover" : true,
59526         /**
59527          * @event mouseout
59528          * The raw mouseout event for the entire grid.
59529          * @param {Roo.EventObject} e
59530          */
59531         "mouseout" : true,
59532         /**
59533          * @event keypress
59534          * The raw keypress event for the entire grid.
59535          * @param {Roo.EventObject} e
59536          */
59537         "keypress" : true,
59538         /**
59539          * @event keydown
59540          * The raw keydown event for the entire grid.
59541          * @param {Roo.EventObject} e
59542          */
59543         "keydown" : true,
59544
59545         // custom events
59546
59547         /**
59548          * @event cellclick
59549          * Fires when a cell is clicked
59550          * @param {Grid} this
59551          * @param {Number} rowIndex
59552          * @param {Number} columnIndex
59553          * @param {Roo.EventObject} e
59554          */
59555         "cellclick" : true,
59556         /**
59557          * @event celldblclick
59558          * Fires when a cell is double clicked
59559          * @param {Grid} this
59560          * @param {Number} rowIndex
59561          * @param {Number} columnIndex
59562          * @param {Roo.EventObject} e
59563          */
59564         "celldblclick" : true,
59565         /**
59566          * @event rowclick
59567          * Fires when a row is clicked
59568          * @param {Grid} this
59569          * @param {Number} rowIndex
59570          * @param {Roo.EventObject} e
59571          */
59572         "rowclick" : true,
59573         /**
59574          * @event rowdblclick
59575          * Fires when a row is double clicked
59576          * @param {Grid} this
59577          * @param {Number} rowIndex
59578          * @param {Roo.EventObject} e
59579          */
59580         "rowdblclick" : true,
59581         /**
59582          * @event headerclick
59583          * Fires when a header is clicked
59584          * @param {Grid} this
59585          * @param {Number} columnIndex
59586          * @param {Roo.EventObject} e
59587          */
59588         "headerclick" : true,
59589         /**
59590          * @event headerdblclick
59591          * Fires when a header cell is double clicked
59592          * @param {Grid} this
59593          * @param {Number} columnIndex
59594          * @param {Roo.EventObject} e
59595          */
59596         "headerdblclick" : true,
59597         /**
59598          * @event rowcontextmenu
59599          * Fires when a row is right clicked
59600          * @param {Grid} this
59601          * @param {Number} rowIndex
59602          * @param {Roo.EventObject} e
59603          */
59604         "rowcontextmenu" : true,
59605         /**
59606          * @event cellcontextmenu
59607          * Fires when a cell is right clicked
59608          * @param {Grid} this
59609          * @param {Number} rowIndex
59610          * @param {Number} cellIndex
59611          * @param {Roo.EventObject} e
59612          */
59613          "cellcontextmenu" : true,
59614         /**
59615          * @event headercontextmenu
59616          * Fires when a header is right clicked
59617          * @param {Grid} this
59618          * @param {Number} columnIndex
59619          * @param {Roo.EventObject} e
59620          */
59621         "headercontextmenu" : true,
59622         /**
59623          * @event bodyscroll
59624          * Fires when the body element is scrolled
59625          * @param {Number} scrollLeft
59626          * @param {Number} scrollTop
59627          */
59628         "bodyscroll" : true,
59629         /**
59630          * @event columnresize
59631          * Fires when the user resizes a column
59632          * @param {Number} columnIndex
59633          * @param {Number} newSize
59634          */
59635         "columnresize" : true,
59636         /**
59637          * @event columnmove
59638          * Fires when the user moves a column
59639          * @param {Number} oldIndex
59640          * @param {Number} newIndex
59641          */
59642         "columnmove" : true,
59643         /**
59644          * @event startdrag
59645          * Fires when row(s) start being dragged
59646          * @param {Grid} this
59647          * @param {Roo.GridDD} dd The drag drop object
59648          * @param {event} e The raw browser event
59649          */
59650         "startdrag" : true,
59651         /**
59652          * @event enddrag
59653          * Fires when a drag operation is complete
59654          * @param {Grid} this
59655          * @param {Roo.GridDD} dd The drag drop object
59656          * @param {event} e The raw browser event
59657          */
59658         "enddrag" : true,
59659         /**
59660          * @event dragdrop
59661          * Fires when dragged row(s) are dropped on a valid DD target
59662          * @param {Grid} this
59663          * @param {Roo.GridDD} dd The drag drop object
59664          * @param {String} targetId The target drag drop object
59665          * @param {event} e The raw browser event
59666          */
59667         "dragdrop" : true,
59668         /**
59669          * @event dragover
59670          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59671          * @param {Grid} this
59672          * @param {Roo.GridDD} dd The drag drop object
59673          * @param {String} targetId The target drag drop object
59674          * @param {event} e The raw browser event
59675          */
59676         "dragover" : true,
59677         /**
59678          * @event dragenter
59679          *  Fires when the dragged row(s) first cross another DD target while being dragged
59680          * @param {Grid} this
59681          * @param {Roo.GridDD} dd The drag drop object
59682          * @param {String} targetId The target drag drop object
59683          * @param {event} e The raw browser event
59684          */
59685         "dragenter" : true,
59686         /**
59687          * @event dragout
59688          * Fires when the dragged row(s) leave another DD target while being dragged
59689          * @param {Grid} this
59690          * @param {Roo.GridDD} dd The drag drop object
59691          * @param {String} targetId The target drag drop object
59692          * @param {event} e The raw browser event
59693          */
59694         "dragout" : true,
59695         /**
59696          * @event rowclass
59697          * Fires when a row is rendered, so you can change add a style to it.
59698          * @param {GridView} gridview   The grid view
59699          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59700          */
59701         'rowclass' : true,
59702
59703         /**
59704          * @event render
59705          * Fires when the grid is rendered
59706          * @param {Grid} grid
59707          */
59708         'render' : true,
59709             /**
59710              * @event select
59711              * Fires when a date is selected
59712              * @param {DatePicker} this
59713              * @param {Date} date The selected date
59714              */
59715         'select': true,
59716         /**
59717              * @event monthchange
59718              * Fires when the displayed month changes 
59719              * @param {DatePicker} this
59720              * @param {Date} date The selected month
59721              */
59722         'monthchange': true,
59723         /**
59724              * @event evententer
59725              * Fires when mouse over an event
59726              * @param {Calendar} this
59727              * @param {event} Event
59728              */
59729         'evententer': true,
59730         /**
59731              * @event eventleave
59732              * Fires when the mouse leaves an
59733              * @param {Calendar} this
59734              * @param {event}
59735              */
59736         'eventleave': true,
59737         /**
59738              * @event eventclick
59739              * Fires when the mouse click an
59740              * @param {Calendar} this
59741              * @param {event}
59742              */
59743         'eventclick': true,
59744         /**
59745              * @event eventrender
59746              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59747              * @param {Calendar} this
59748              * @param {data} data to be modified
59749              */
59750         'eventrender': true
59751         
59752     });
59753
59754     Roo.grid.Grid.superclass.constructor.call(this);
59755     this.on('render', function() {
59756         this.view.el.addClass('x-grid-cal'); 
59757         
59758         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59759
59760     },this);
59761     
59762     if (!Roo.grid.Calendar.style) {
59763         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59764             
59765             
59766             '.x-grid-cal .x-grid-col' :  {
59767                 height: 'auto !important',
59768                 'vertical-align': 'top'
59769             },
59770             '.x-grid-cal  .fc-event-hori' : {
59771                 height: '14px'
59772             }
59773              
59774             
59775         }, Roo.id());
59776     }
59777
59778     
59779     
59780 };
59781 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59782     /**
59783      * @cfg {Store} eventStore The store that loads events.
59784      */
59785     eventStore : 25,
59786
59787      
59788     activeDate : false,
59789     startDay : 0,
59790     autoWidth : true,
59791     monitorWindowResize : false,
59792
59793     
59794     resizeColumns : function() {
59795         var col = (this.view.el.getWidth() / 7) - 3;
59796         // loop through cols, and setWidth
59797         for(var i =0 ; i < 7 ; i++){
59798             this.cm.setColumnWidth(i, col);
59799         }
59800     },
59801      setDate :function(date) {
59802         
59803         Roo.log('setDate?');
59804         
59805         this.resizeColumns();
59806         var vd = this.activeDate;
59807         this.activeDate = date;
59808 //        if(vd && this.el){
59809 //            var t = date.getTime();
59810 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59811 //                Roo.log('using add remove');
59812 //                
59813 //                this.fireEvent('monthchange', this, date);
59814 //                
59815 //                this.cells.removeClass("fc-state-highlight");
59816 //                this.cells.each(function(c){
59817 //                   if(c.dateValue == t){
59818 //                       c.addClass("fc-state-highlight");
59819 //                       setTimeout(function(){
59820 //                            try{c.dom.firstChild.focus();}catch(e){}
59821 //                       }, 50);
59822 //                       return false;
59823 //                   }
59824 //                   return true;
59825 //                });
59826 //                return;
59827 //            }
59828 //        }
59829         
59830         var days = date.getDaysInMonth();
59831         
59832         var firstOfMonth = date.getFirstDateOfMonth();
59833         var startingPos = firstOfMonth.getDay()-this.startDay;
59834         
59835         if(startingPos < this.startDay){
59836             startingPos += 7;
59837         }
59838         
59839         var pm = date.add(Date.MONTH, -1);
59840         var prevStart = pm.getDaysInMonth()-startingPos;
59841 //        
59842         
59843         
59844         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59845         
59846         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59847         //this.cells.addClassOnOver('fc-state-hover');
59848         
59849         var cells = this.cells.elements;
59850         var textEls = this.textNodes;
59851         
59852         //Roo.each(cells, function(cell){
59853         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59854         //});
59855         
59856         days += startingPos;
59857
59858         // convert everything to numbers so it's fast
59859         var day = 86400000;
59860         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59861         //Roo.log(d);
59862         //Roo.log(pm);
59863         //Roo.log(prevStart);
59864         
59865         var today = new Date().clearTime().getTime();
59866         var sel = date.clearTime().getTime();
59867         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59868         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59869         var ddMatch = this.disabledDatesRE;
59870         var ddText = this.disabledDatesText;
59871         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59872         var ddaysText = this.disabledDaysText;
59873         var format = this.format;
59874         
59875         var setCellClass = function(cal, cell){
59876             
59877             //Roo.log('set Cell Class');
59878             cell.title = "";
59879             var t = d.getTime();
59880             
59881             //Roo.log(d);
59882             
59883             
59884             cell.dateValue = t;
59885             if(t == today){
59886                 cell.className += " fc-today";
59887                 cell.className += " fc-state-highlight";
59888                 cell.title = cal.todayText;
59889             }
59890             if(t == sel){
59891                 // disable highlight in other month..
59892                 cell.className += " fc-state-highlight";
59893                 
59894             }
59895             // disabling
59896             if(t < min) {
59897                 //cell.className = " fc-state-disabled";
59898                 cell.title = cal.minText;
59899                 return;
59900             }
59901             if(t > max) {
59902                 //cell.className = " fc-state-disabled";
59903                 cell.title = cal.maxText;
59904                 return;
59905             }
59906             if(ddays){
59907                 if(ddays.indexOf(d.getDay()) != -1){
59908                     // cell.title = ddaysText;
59909                    // cell.className = " fc-state-disabled";
59910                 }
59911             }
59912             if(ddMatch && format){
59913                 var fvalue = d.dateFormat(format);
59914                 if(ddMatch.test(fvalue)){
59915                     cell.title = ddText.replace("%0", fvalue);
59916                    cell.className = " fc-state-disabled";
59917                 }
59918             }
59919             
59920             if (!cell.initialClassName) {
59921                 cell.initialClassName = cell.dom.className;
59922             }
59923             
59924             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59925         };
59926
59927         var i = 0;
59928         
59929         for(; i < startingPos; i++) {
59930             cells[i].dayName =  (++prevStart);
59931             Roo.log(textEls[i]);
59932             d.setDate(d.getDate()+1);
59933             
59934             //cells[i].className = "fc-past fc-other-month";
59935             setCellClass(this, cells[i]);
59936         }
59937         
59938         var intDay = 0;
59939         
59940         for(; i < days; i++){
59941             intDay = i - startingPos + 1;
59942             cells[i].dayName =  (intDay);
59943             d.setDate(d.getDate()+1);
59944             
59945             cells[i].className = ''; // "x-date-active";
59946             setCellClass(this, cells[i]);
59947         }
59948         var extraDays = 0;
59949         
59950         for(; i < 42; i++) {
59951             //textEls[i].innerHTML = (++extraDays);
59952             
59953             d.setDate(d.getDate()+1);
59954             cells[i].dayName = (++extraDays);
59955             cells[i].className = "fc-future fc-other-month";
59956             setCellClass(this, cells[i]);
59957         }
59958         
59959         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59960         
59961         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59962         
59963         // this will cause all the cells to mis
59964         var rows= [];
59965         var i =0;
59966         for (var r = 0;r < 6;r++) {
59967             for (var c =0;c < 7;c++) {
59968                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59969             }    
59970         }
59971         
59972         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59973         for(i=0;i<cells.length;i++) {
59974             
59975             this.cells.elements[i].dayName = cells[i].dayName ;
59976             this.cells.elements[i].className = cells[i].className;
59977             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59978             this.cells.elements[i].title = cells[i].title ;
59979             this.cells.elements[i].dateValue = cells[i].dateValue ;
59980         }
59981         
59982         
59983         
59984         
59985         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59986         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59987         
59988         ////if(totalRows != 6){
59989             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59990            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59991        // }
59992         
59993         this.fireEvent('monthchange', this, date);
59994         
59995         
59996     },
59997  /**
59998      * Returns the grid's SelectionModel.
59999      * @return {SelectionModel}
60000      */
60001     getSelectionModel : function(){
60002         if(!this.selModel){
60003             this.selModel = new Roo.grid.CellSelectionModel();
60004         }
60005         return this.selModel;
60006     },
60007
60008     load: function() {
60009         this.eventStore.load()
60010         
60011         
60012         
60013     },
60014     
60015     findCell : function(dt) {
60016         dt = dt.clearTime().getTime();
60017         var ret = false;
60018         this.cells.each(function(c){
60019             //Roo.log("check " +c.dateValue + '?=' + dt);
60020             if(c.dateValue == dt){
60021                 ret = c;
60022                 return false;
60023             }
60024             return true;
60025         });
60026         
60027         return ret;
60028     },
60029     
60030     findCells : function(rec) {
60031         var s = rec.data.start_dt.clone().clearTime().getTime();
60032        // Roo.log(s);
60033         var e= rec.data.end_dt.clone().clearTime().getTime();
60034        // Roo.log(e);
60035         var ret = [];
60036         this.cells.each(function(c){
60037              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60038             
60039             if(c.dateValue > e){
60040                 return ;
60041             }
60042             if(c.dateValue < s){
60043                 return ;
60044             }
60045             ret.push(c);
60046         });
60047         
60048         return ret;    
60049     },
60050     
60051     findBestRow: function(cells)
60052     {
60053         var ret = 0;
60054         
60055         for (var i =0 ; i < cells.length;i++) {
60056             ret  = Math.max(cells[i].rows || 0,ret);
60057         }
60058         return ret;
60059         
60060     },
60061     
60062     
60063     addItem : function(rec)
60064     {
60065         // look for vertical location slot in
60066         var cells = this.findCells(rec);
60067         
60068         rec.row = this.findBestRow(cells);
60069         
60070         // work out the location.
60071         
60072         var crow = false;
60073         var rows = [];
60074         for(var i =0; i < cells.length; i++) {
60075             if (!crow) {
60076                 crow = {
60077                     start : cells[i],
60078                     end :  cells[i]
60079                 };
60080                 continue;
60081             }
60082             if (crow.start.getY() == cells[i].getY()) {
60083                 // on same row.
60084                 crow.end = cells[i];
60085                 continue;
60086             }
60087             // different row.
60088             rows.push(crow);
60089             crow = {
60090                 start: cells[i],
60091                 end : cells[i]
60092             };
60093             
60094         }
60095         
60096         rows.push(crow);
60097         rec.els = [];
60098         rec.rows = rows;
60099         rec.cells = cells;
60100         for (var i = 0; i < cells.length;i++) {
60101             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60102             
60103         }
60104         
60105         
60106     },
60107     
60108     clearEvents: function() {
60109         
60110         if (!this.eventStore.getCount()) {
60111             return;
60112         }
60113         // reset number of rows in cells.
60114         Roo.each(this.cells.elements, function(c){
60115             c.rows = 0;
60116         });
60117         
60118         this.eventStore.each(function(e) {
60119             this.clearEvent(e);
60120         },this);
60121         
60122     },
60123     
60124     clearEvent : function(ev)
60125     {
60126         if (ev.els) {
60127             Roo.each(ev.els, function(el) {
60128                 el.un('mouseenter' ,this.onEventEnter, this);
60129                 el.un('mouseleave' ,this.onEventLeave, this);
60130                 el.remove();
60131             },this);
60132             ev.els = [];
60133         }
60134     },
60135     
60136     
60137     renderEvent : function(ev,ctr) {
60138         if (!ctr) {
60139              ctr = this.view.el.select('.fc-event-container',true).first();
60140         }
60141         
60142          
60143         this.clearEvent(ev);
60144             //code
60145        
60146         
60147         
60148         ev.els = [];
60149         var cells = ev.cells;
60150         var rows = ev.rows;
60151         this.fireEvent('eventrender', this, ev);
60152         
60153         for(var i =0; i < rows.length; i++) {
60154             
60155             cls = '';
60156             if (i == 0) {
60157                 cls += ' fc-event-start';
60158             }
60159             if ((i+1) == rows.length) {
60160                 cls += ' fc-event-end';
60161             }
60162             
60163             //Roo.log(ev.data);
60164             // how many rows should it span..
60165             var cg = this.eventTmpl.append(ctr,Roo.apply({
60166                 fccls : cls
60167                 
60168             }, ev.data) , true);
60169             
60170             
60171             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60172             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60173             cg.on('click', this.onEventClick, this, ev);
60174             
60175             ev.els.push(cg);
60176             
60177             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60178             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60179             //Roo.log(cg);
60180              
60181             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60182             cg.setWidth(ebox.right - sbox.x -2);
60183         }
60184     },
60185     
60186     renderEvents: function()
60187     {   
60188         // first make sure there is enough space..
60189         
60190         if (!this.eventTmpl) {
60191             this.eventTmpl = new Roo.Template(
60192                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60193                     '<div class="fc-event-inner">' +
60194                         '<span class="fc-event-time">{time}</span>' +
60195                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60196                     '</div>' +
60197                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60198                 '</div>'
60199             );
60200                 
60201         }
60202                
60203         
60204         
60205         this.cells.each(function(c) {
60206             //Roo.log(c.select('.fc-day-content div',true).first());
60207             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60208         });
60209         
60210         var ctr = this.view.el.select('.fc-event-container',true).first();
60211         
60212         var cls;
60213         this.eventStore.each(function(ev){
60214             
60215             this.renderEvent(ev);
60216              
60217              
60218         }, this);
60219         this.view.layout();
60220         
60221     },
60222     
60223     onEventEnter: function (e, el,event,d) {
60224         this.fireEvent('evententer', this, el, event);
60225     },
60226     
60227     onEventLeave: function (e, el,event,d) {
60228         this.fireEvent('eventleave', this, el, event);
60229     },
60230     
60231     onEventClick: function (e, el,event,d) {
60232         this.fireEvent('eventclick', this, el, event);
60233     },
60234     
60235     onMonthChange: function () {
60236         this.store.load();
60237     },
60238     
60239     onLoad: function () {
60240         
60241         //Roo.log('calendar onload');
60242 //         
60243         if(this.eventStore.getCount() > 0){
60244             
60245            
60246             
60247             this.eventStore.each(function(d){
60248                 
60249                 
60250                 // FIXME..
60251                 var add =   d.data;
60252                 if (typeof(add.end_dt) == 'undefined')  {
60253                     Roo.log("Missing End time in calendar data: ");
60254                     Roo.log(d);
60255                     return;
60256                 }
60257                 if (typeof(add.start_dt) == 'undefined')  {
60258                     Roo.log("Missing Start time in calendar data: ");
60259                     Roo.log(d);
60260                     return;
60261                 }
60262                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60263                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60264                 add.id = add.id || d.id;
60265                 add.title = add.title || '??';
60266                 
60267                 this.addItem(d);
60268                 
60269              
60270             },this);
60271         }
60272         
60273         this.renderEvents();
60274     }
60275     
60276
60277 });
60278 /*
60279  grid : {
60280                 xtype: 'Grid',
60281                 xns: Roo.grid,
60282                 listeners : {
60283                     render : function ()
60284                     {
60285                         _this.grid = this;
60286                         
60287                         if (!this.view.el.hasClass('course-timesheet')) {
60288                             this.view.el.addClass('course-timesheet');
60289                         }
60290                         if (this.tsStyle) {
60291                             this.ds.load({});
60292                             return; 
60293                         }
60294                         Roo.log('width');
60295                         Roo.log(_this.grid.view.el.getWidth());
60296                         
60297                         
60298                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60299                             '.course-timesheet .x-grid-row' : {
60300                                 height: '80px'
60301                             },
60302                             '.x-grid-row td' : {
60303                                 'vertical-align' : 0
60304                             },
60305                             '.course-edit-link' : {
60306                                 'color' : 'blue',
60307                                 'text-overflow' : 'ellipsis',
60308                                 'overflow' : 'hidden',
60309                                 'white-space' : 'nowrap',
60310                                 'cursor' : 'pointer'
60311                             },
60312                             '.sub-link' : {
60313                                 'color' : 'green'
60314                             },
60315                             '.de-act-sup-link' : {
60316                                 'color' : 'purple',
60317                                 'text-decoration' : 'line-through'
60318                             },
60319                             '.de-act-link' : {
60320                                 'color' : 'red',
60321                                 'text-decoration' : 'line-through'
60322                             },
60323                             '.course-timesheet .course-highlight' : {
60324                                 'border-top-style': 'dashed !important',
60325                                 'border-bottom-bottom': 'dashed !important'
60326                             },
60327                             '.course-timesheet .course-item' : {
60328                                 'font-family'   : 'tahoma, arial, helvetica',
60329                                 'font-size'     : '11px',
60330                                 'overflow'      : 'hidden',
60331                                 'padding-left'  : '10px',
60332                                 'padding-right' : '10px',
60333                                 'padding-top' : '10px' 
60334                             }
60335                             
60336                         }, Roo.id());
60337                                 this.ds.load({});
60338                     }
60339                 },
60340                 autoWidth : true,
60341                 monitorWindowResize : false,
60342                 cellrenderer : function(v,x,r)
60343                 {
60344                     return v;
60345                 },
60346                 sm : {
60347                     xtype: 'CellSelectionModel',
60348                     xns: Roo.grid
60349                 },
60350                 dataSource : {
60351                     xtype: 'Store',
60352                     xns: Roo.data,
60353                     listeners : {
60354                         beforeload : function (_self, options)
60355                         {
60356                             options.params = options.params || {};
60357                             options.params._month = _this.monthField.getValue();
60358                             options.params.limit = 9999;
60359                             options.params['sort'] = 'when_dt';    
60360                             options.params['dir'] = 'ASC';    
60361                             this.proxy.loadResponse = this.loadResponse;
60362                             Roo.log("load?");
60363                             //this.addColumns();
60364                         },
60365                         load : function (_self, records, options)
60366                         {
60367                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60368                                 // if you click on the translation.. you can edit it...
60369                                 var el = Roo.get(this);
60370                                 var id = el.dom.getAttribute('data-id');
60371                                 var d = el.dom.getAttribute('data-date');
60372                                 var t = el.dom.getAttribute('data-time');
60373                                 //var id = this.child('span').dom.textContent;
60374                                 
60375                                 //Roo.log(this);
60376                                 Pman.Dialog.CourseCalendar.show({
60377                                     id : id,
60378                                     when_d : d,
60379                                     when_t : t,
60380                                     productitem_active : id ? 1 : 0
60381                                 }, function() {
60382                                     _this.grid.ds.load({});
60383                                 });
60384                            
60385                            });
60386                            
60387                            _this.panel.fireEvent('resize', [ '', '' ]);
60388                         }
60389                     },
60390                     loadResponse : function(o, success, response){
60391                             // this is overridden on before load..
60392                             
60393                             Roo.log("our code?");       
60394                             //Roo.log(success);
60395                             //Roo.log(response)
60396                             delete this.activeRequest;
60397                             if(!success){
60398                                 this.fireEvent("loadexception", this, o, response);
60399                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60400                                 return;
60401                             }
60402                             var result;
60403                             try {
60404                                 result = o.reader.read(response);
60405                             }catch(e){
60406                                 Roo.log("load exception?");
60407                                 this.fireEvent("loadexception", this, o, response, e);
60408                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60409                                 return;
60410                             }
60411                             Roo.log("ready...");        
60412                             // loop through result.records;
60413                             // and set this.tdate[date] = [] << array of records..
60414                             _this.tdata  = {};
60415                             Roo.each(result.records, function(r){
60416                                 //Roo.log(r.data);
60417                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60418                                     _this.tdata[r.data.when_dt.format('j')] = [];
60419                                 }
60420                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60421                             });
60422                             
60423                             //Roo.log(_this.tdata);
60424                             
60425                             result.records = [];
60426                             result.totalRecords = 6;
60427                     
60428                             // let's generate some duumy records for the rows.
60429                             //var st = _this.dateField.getValue();
60430                             
60431                             // work out monday..
60432                             //st = st.add(Date.DAY, -1 * st.format('w'));
60433                             
60434                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60435                             
60436                             var firstOfMonth = date.getFirstDayOfMonth();
60437                             var days = date.getDaysInMonth();
60438                             var d = 1;
60439                             var firstAdded = false;
60440                             for (var i = 0; i < result.totalRecords ; i++) {
60441                                 //var d= st.add(Date.DAY, i);
60442                                 var row = {};
60443                                 var added = 0;
60444                                 for(var w = 0 ; w < 7 ; w++){
60445                                     if(!firstAdded && firstOfMonth != w){
60446                                         continue;
60447                                     }
60448                                     if(d > days){
60449                                         continue;
60450                                     }
60451                                     firstAdded = true;
60452                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60453                                     row['weekday'+w] = String.format(
60454                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60455                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60456                                                     d,
60457                                                     date.format('Y-m-')+dd
60458                                                 );
60459                                     added++;
60460                                     if(typeof(_this.tdata[d]) != 'undefined'){
60461                                         Roo.each(_this.tdata[d], function(r){
60462                                             var is_sub = '';
60463                                             var deactive = '';
60464                                             var id = r.id;
60465                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60466                                             if(r.parent_id*1>0){
60467                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60468                                                 id = r.parent_id;
60469                                             }
60470                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60471                                                 deactive = 'de-act-link';
60472                                             }
60473                                             
60474                                             row['weekday'+w] += String.format(
60475                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60476                                                     id, //0
60477                                                     r.product_id_name, //1
60478                                                     r.when_dt.format('h:ia'), //2
60479                                                     is_sub, //3
60480                                                     deactive, //4
60481                                                     desc // 5
60482                                             );
60483                                         });
60484                                     }
60485                                     d++;
60486                                 }
60487                                 
60488                                 // only do this if something added..
60489                                 if(added > 0){ 
60490                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60491                                 }
60492                                 
60493                                 
60494                                 // push it twice. (second one with an hour..
60495                                 
60496                             }
60497                             //Roo.log(result);
60498                             this.fireEvent("load", this, o, o.request.arg);
60499                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60500                         },
60501                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60502                     proxy : {
60503                         xtype: 'HttpProxy',
60504                         xns: Roo.data,
60505                         method : 'GET',
60506                         url : baseURL + '/Roo/Shop_course.php'
60507                     },
60508                     reader : {
60509                         xtype: 'JsonReader',
60510                         xns: Roo.data,
60511                         id : 'id',
60512                         fields : [
60513                             {
60514                                 'name': 'id',
60515                                 'type': 'int'
60516                             },
60517                             {
60518                                 'name': 'when_dt',
60519                                 'type': 'string'
60520                             },
60521                             {
60522                                 'name': 'end_dt',
60523                                 'type': 'string'
60524                             },
60525                             {
60526                                 'name': 'parent_id',
60527                                 'type': 'int'
60528                             },
60529                             {
60530                                 'name': 'product_id',
60531                                 'type': 'int'
60532                             },
60533                             {
60534                                 'name': 'productitem_id',
60535                                 'type': 'int'
60536                             },
60537                             {
60538                                 'name': 'guid',
60539                                 'type': 'int'
60540                             }
60541                         ]
60542                     }
60543                 },
60544                 toolbar : {
60545                     xtype: 'Toolbar',
60546                     xns: Roo,
60547                     items : [
60548                         {
60549                             xtype: 'Button',
60550                             xns: Roo.Toolbar,
60551                             listeners : {
60552                                 click : function (_self, e)
60553                                 {
60554                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60555                                     sd.setMonth(sd.getMonth()-1);
60556                                     _this.monthField.setValue(sd.format('Y-m-d'));
60557                                     _this.grid.ds.load({});
60558                                 }
60559                             },
60560                             text : "Back"
60561                         },
60562                         {
60563                             xtype: 'Separator',
60564                             xns: Roo.Toolbar
60565                         },
60566                         {
60567                             xtype: 'MonthField',
60568                             xns: Roo.form,
60569                             listeners : {
60570                                 render : function (_self)
60571                                 {
60572                                     _this.monthField = _self;
60573                                    // _this.monthField.set  today
60574                                 },
60575                                 select : function (combo, date)
60576                                 {
60577                                     _this.grid.ds.load({});
60578                                 }
60579                             },
60580                             value : (function() { return new Date(); })()
60581                         },
60582                         {
60583                             xtype: 'Separator',
60584                             xns: Roo.Toolbar
60585                         },
60586                         {
60587                             xtype: 'TextItem',
60588                             xns: Roo.Toolbar,
60589                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60590                         },
60591                         {
60592                             xtype: 'Fill',
60593                             xns: Roo.Toolbar
60594                         },
60595                         {
60596                             xtype: 'Button',
60597                             xns: Roo.Toolbar,
60598                             listeners : {
60599                                 click : function (_self, e)
60600                                 {
60601                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60602                                     sd.setMonth(sd.getMonth()+1);
60603                                     _this.monthField.setValue(sd.format('Y-m-d'));
60604                                     _this.grid.ds.load({});
60605                                 }
60606                             },
60607                             text : "Next"
60608                         }
60609                     ]
60610                 },
60611                  
60612             }
60613         };
60614         
60615         *//*
60616  * Based on:
60617  * Ext JS Library 1.1.1
60618  * Copyright(c) 2006-2007, Ext JS, LLC.
60619  *
60620  * Originally Released Under LGPL - original licence link has changed is not relivant.
60621  *
60622  * Fork - LGPL
60623  * <script type="text/javascript">
60624  */
60625  
60626 /**
60627  * @class Roo.LoadMask
60628  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60629  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60630  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60631  * element's UpdateManager load indicator and will be destroyed after the initial load.
60632  * @constructor
60633  * Create a new LoadMask
60634  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60635  * @param {Object} config The config object
60636  */
60637 Roo.LoadMask = function(el, config){
60638     this.el = Roo.get(el);
60639     Roo.apply(this, config);
60640     if(this.store){
60641         this.store.on('beforeload', this.onBeforeLoad, this);
60642         this.store.on('load', this.onLoad, this);
60643         this.store.on('loadexception', this.onLoadException, this);
60644         this.removeMask = false;
60645     }else{
60646         var um = this.el.getUpdateManager();
60647         um.showLoadIndicator = false; // disable the default indicator
60648         um.on('beforeupdate', this.onBeforeLoad, this);
60649         um.on('update', this.onLoad, this);
60650         um.on('failure', this.onLoad, this);
60651         this.removeMask = true;
60652     }
60653 };
60654
60655 Roo.LoadMask.prototype = {
60656     /**
60657      * @cfg {Boolean} removeMask
60658      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60659      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60660      */
60661     /**
60662      * @cfg {String} msg
60663      * The text to display in a centered loading message box (defaults to 'Loading...')
60664      */
60665     msg : 'Loading...',
60666     /**
60667      * @cfg {String} msgCls
60668      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60669      */
60670     msgCls : 'x-mask-loading',
60671
60672     /**
60673      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60674      * @type Boolean
60675      */
60676     disabled: false,
60677
60678     /**
60679      * Disables the mask to prevent it from being displayed
60680      */
60681     disable : function(){
60682        this.disabled = true;
60683     },
60684
60685     /**
60686      * Enables the mask so that it can be displayed
60687      */
60688     enable : function(){
60689         this.disabled = false;
60690     },
60691     
60692     onLoadException : function()
60693     {
60694         Roo.log(arguments);
60695         
60696         if (typeof(arguments[3]) != 'undefined') {
60697             Roo.MessageBox.alert("Error loading",arguments[3]);
60698         } 
60699         /*
60700         try {
60701             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60702                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60703             }   
60704         } catch(e) {
60705             
60706         }
60707         */
60708     
60709         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60710     },
60711     // private
60712     onLoad : function()
60713     {
60714         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60715     },
60716
60717     // private
60718     onBeforeLoad : function(){
60719         if(!this.disabled){
60720             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60721         }
60722     },
60723
60724     // private
60725     destroy : function(){
60726         if(this.store){
60727             this.store.un('beforeload', this.onBeforeLoad, this);
60728             this.store.un('load', this.onLoad, this);
60729             this.store.un('loadexception', this.onLoadException, this);
60730         }else{
60731             var um = this.el.getUpdateManager();
60732             um.un('beforeupdate', this.onBeforeLoad, this);
60733             um.un('update', this.onLoad, this);
60734             um.un('failure', this.onLoad, this);
60735         }
60736     }
60737 };/*
60738  * Based on:
60739  * Ext JS Library 1.1.1
60740  * Copyright(c) 2006-2007, Ext JS, LLC.
60741  *
60742  * Originally Released Under LGPL - original licence link has changed is not relivant.
60743  *
60744  * Fork - LGPL
60745  * <script type="text/javascript">
60746  */
60747
60748
60749 /**
60750  * @class Roo.XTemplate
60751  * @extends Roo.Template
60752  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60753 <pre><code>
60754 var t = new Roo.XTemplate(
60755         '&lt;select name="{name}"&gt;',
60756                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60757         '&lt;/select&gt;'
60758 );
60759  
60760 // then append, applying the master template values
60761  </code></pre>
60762  *
60763  * Supported features:
60764  *
60765  *  Tags:
60766
60767 <pre><code>
60768       {a_variable} - output encoded.
60769       {a_variable.format:("Y-m-d")} - call a method on the variable
60770       {a_variable:raw} - unencoded output
60771       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60772       {a_variable:this.method_on_template(...)} - call a method on the template object.
60773  
60774 </code></pre>
60775  *  The tpl tag:
60776 <pre><code>
60777         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60778         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60779         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60780         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60781   
60782         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60783         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60784 </code></pre>
60785  *      
60786  */
60787 Roo.XTemplate = function()
60788 {
60789     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60790     if (this.html) {
60791         this.compile();
60792     }
60793 };
60794
60795
60796 Roo.extend(Roo.XTemplate, Roo.Template, {
60797
60798     /**
60799      * The various sub templates
60800      */
60801     tpls : false,
60802     /**
60803      *
60804      * basic tag replacing syntax
60805      * WORD:WORD()
60806      *
60807      * // you can fake an object call by doing this
60808      *  x.t:(test,tesT) 
60809      * 
60810      */
60811     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60812
60813     /**
60814      * compile the template
60815      *
60816      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60817      *
60818      */
60819     compile: function()
60820     {
60821         var s = this.html;
60822      
60823         s = ['<tpl>', s, '</tpl>'].join('');
60824     
60825         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60826             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60827             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60828             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60829             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60830             m,
60831             id     = 0,
60832             tpls   = [];
60833     
60834         while(true == !!(m = s.match(re))){
60835             var forMatch   = m[0].match(nameRe),
60836                 ifMatch   = m[0].match(ifRe),
60837                 execMatch   = m[0].match(execRe),
60838                 namedMatch   = m[0].match(namedRe),
60839                 
60840                 exp  = null, 
60841                 fn   = null,
60842                 exec = null,
60843                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60844                 
60845             if (ifMatch) {
60846                 // if - puts fn into test..
60847                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60848                 if(exp){
60849                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60850                 }
60851             }
60852             
60853             if (execMatch) {
60854                 // exec - calls a function... returns empty if true is  returned.
60855                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60856                 if(exp){
60857                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60858                 }
60859             }
60860             
60861             
60862             if (name) {
60863                 // for = 
60864                 switch(name){
60865                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60866                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60867                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60868                 }
60869             }
60870             var uid = namedMatch ? namedMatch[1] : id;
60871             
60872             
60873             tpls.push({
60874                 id:     namedMatch ? namedMatch[1] : id,
60875                 target: name,
60876                 exec:   exec,
60877                 test:   fn,
60878                 body:   m[1] || ''
60879             });
60880             if (namedMatch) {
60881                 s = s.replace(m[0], '');
60882             } else { 
60883                 s = s.replace(m[0], '{xtpl'+ id + '}');
60884             }
60885             ++id;
60886         }
60887         this.tpls = [];
60888         for(var i = tpls.length-1; i >= 0; --i){
60889             this.compileTpl(tpls[i]);
60890             this.tpls[tpls[i].id] = tpls[i];
60891         }
60892         this.master = tpls[tpls.length-1];
60893         return this;
60894     },
60895     /**
60896      * same as applyTemplate, except it's done to one of the subTemplates
60897      * when using named templates, you can do:
60898      *
60899      * var str = pl.applySubTemplate('your-name', values);
60900      *
60901      * 
60902      * @param {Number} id of the template
60903      * @param {Object} values to apply to template
60904      * @param {Object} parent (normaly the instance of this object)
60905      */
60906     applySubTemplate : function(id, values, parent)
60907     {
60908         
60909         
60910         var t = this.tpls[id];
60911         
60912         
60913         try { 
60914             if(t.test && !t.test.call(this, values, parent)){
60915                 return '';
60916             }
60917         } catch(e) {
60918             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60919             Roo.log(e.toString());
60920             Roo.log(t.test);
60921             return ''
60922         }
60923         try { 
60924             
60925             if(t.exec && t.exec.call(this, values, parent)){
60926                 return '';
60927             }
60928         } catch(e) {
60929             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60930             Roo.log(e.toString());
60931             Roo.log(t.exec);
60932             return ''
60933         }
60934         try {
60935             var vs = t.target ? t.target.call(this, values, parent) : values;
60936             parent = t.target ? values : parent;
60937             if(t.target && vs instanceof Array){
60938                 var buf = [];
60939                 for(var i = 0, len = vs.length; i < len; i++){
60940                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60941                 }
60942                 return buf.join('');
60943             }
60944             return t.compiled.call(this, vs, parent);
60945         } catch (e) {
60946             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60947             Roo.log(e.toString());
60948             Roo.log(t.compiled);
60949             return '';
60950         }
60951     },
60952
60953     compileTpl : function(tpl)
60954     {
60955         var fm = Roo.util.Format;
60956         var useF = this.disableFormats !== true;
60957         var sep = Roo.isGecko ? "+" : ",";
60958         var undef = function(str) {
60959             Roo.log("Property not found :"  + str);
60960             return '';
60961         };
60962         
60963         var fn = function(m, name, format, args)
60964         {
60965             //Roo.log(arguments);
60966             args = args ? args.replace(/\\'/g,"'") : args;
60967             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60968             if (typeof(format) == 'undefined') {
60969                 format= 'htmlEncode';
60970             }
60971             if (format == 'raw' ) {
60972                 format = false;
60973             }
60974             
60975             if(name.substr(0, 4) == 'xtpl'){
60976                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60977             }
60978             
60979             // build an array of options to determine if value is undefined..
60980             
60981             // basically get 'xxxx.yyyy' then do
60982             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60983             //    (function () { Roo.log("Property not found"); return ''; })() :
60984             //    ......
60985             
60986             var udef_ar = [];
60987             var lookfor = '';
60988             Roo.each(name.split('.'), function(st) {
60989                 lookfor += (lookfor.length ? '.': '') + st;
60990                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60991             });
60992             
60993             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60994             
60995             
60996             if(format && useF){
60997                 
60998                 args = args ? ',' + args : "";
60999                  
61000                 if(format.substr(0, 5) != "this."){
61001                     format = "fm." + format + '(';
61002                 }else{
61003                     format = 'this.call("'+ format.substr(5) + '", ';
61004                     args = ", values";
61005                 }
61006                 
61007                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61008             }
61009              
61010             if (args.length) {
61011                 // called with xxyx.yuu:(test,test)
61012                 // change to ()
61013                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61014             }
61015             // raw.. - :raw modifier..
61016             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61017             
61018         };
61019         var body;
61020         // branched to use + in gecko and [].join() in others
61021         if(Roo.isGecko){
61022             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61023                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61024                     "';};};";
61025         }else{
61026             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61027             body.push(tpl.body.replace(/(\r\n|\n)/g,
61028                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61029             body.push("'].join('');};};");
61030             body = body.join('');
61031         }
61032         
61033         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61034        
61035         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61036         eval(body);
61037         
61038         return this;
61039     },
61040
61041     applyTemplate : function(values){
61042         return this.master.compiled.call(this, values, {});
61043         //var s = this.subs;
61044     },
61045
61046     apply : function(){
61047         return this.applyTemplate.apply(this, arguments);
61048     }
61049
61050  });
61051
61052 Roo.XTemplate.from = function(el){
61053     el = Roo.getDom(el);
61054     return new Roo.XTemplate(el.value || el.innerHTML);
61055 };