roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         //Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 var args = Array.prototype.slice.call(arguments, 0);                
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "") 
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         
7136         // note this is overridden in BS version..
7137         visibilityMode : 1, 
7138         /**
7139          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7140          * @type String
7141          */
7142         defaultUnit : "px",
7143         
7144         /**
7145          * Sets the element's visibility mode. When setVisible() is called it
7146          * will use this to determine whether to set the visibility or the display property.
7147          * @param visMode Element.VISIBILITY or Element.DISPLAY
7148          * @return {Roo.Element} this
7149          */
7150         setVisibilityMode : function(visMode){
7151             this.visibilityMode = visMode;
7152             return this;
7153         },
7154         /**
7155          * Convenience method for setVisibilityMode(Element.DISPLAY)
7156          * @param {String} display (optional) What to set display to when visible
7157          * @return {Roo.Element} this
7158          */
7159         enableDisplayMode : function(display){
7160             this.setVisibilityMode(El.DISPLAY);
7161             if(typeof display != "undefined") { this.originalDisplay = display; }
7162             return this;
7163         },
7164
7165         /**
7166          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7167          * @param {String} selector The simple selector to test
7168          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7169                 search as a number or element (defaults to 10 || document.body)
7170          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7171          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7172          */
7173         findParent : function(simpleSelector, maxDepth, returnEl){
7174             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7175             maxDepth = maxDepth || 50;
7176             if(typeof maxDepth != "number"){
7177                 stopEl = Roo.getDom(maxDepth);
7178                 maxDepth = 10;
7179             }
7180             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7181                 if(dq.is(p, simpleSelector)){
7182                     return returnEl ? Roo.get(p) : p;
7183                 }
7184                 depth++;
7185                 p = p.parentNode;
7186             }
7187             return null;
7188         },
7189
7190
7191         /**
7192          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParentNode : function(simpleSelector, maxDepth, returnEl){
7200             var p = Roo.fly(this.dom.parentNode, '_internal');
7201             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7202         },
7203         
7204         /**
7205          * Looks at  the scrollable parent element
7206          */
7207         findScrollableParent : function()
7208         {
7209             var overflowRegex = /(auto|scroll)/;
7210             
7211             if(this.getStyle('position') === 'fixed'){
7212                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7213             }
7214             
7215             var excludeStaticParent = this.getStyle('position') === "absolute";
7216             
7217             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7218                 
7219                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7220                     continue;
7221                 }
7222                 
7223                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7224                     return parent;
7225                 }
7226                 
7227                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7228                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7229                 }
7230             }
7231             
7232             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7233         },
7234
7235         /**
7236          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7237          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7242          */
7243         up : function(simpleSelector, maxDepth){
7244             return this.findParentNode(simpleSelector, maxDepth, true);
7245         },
7246
7247
7248
7249         /**
7250          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7251          * @param {String} selector The simple selector to test
7252          * @return {Boolean} True if this element matches the selector, else false
7253          */
7254         is : function(simpleSelector){
7255             return Roo.DomQuery.is(this.dom, simpleSelector);
7256         },
7257
7258         /**
7259          * Perform animation on this element.
7260          * @param {Object} args The YUI animation control args
7261          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7262          * @param {Function} onComplete (optional) Function to call when animation completes
7263          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7264          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7265          * @return {Roo.Element} this
7266          */
7267         animate : function(args, duration, onComplete, easing, animType){
7268             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7269             return this;
7270         },
7271
7272         /*
7273          * @private Internal animation call
7274          */
7275         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7276             animType = animType || 'run';
7277             opt = opt || {};
7278             var anim = Roo.lib.Anim[animType](
7279                 this.dom, args,
7280                 (opt.duration || defaultDur) || .35,
7281                 (opt.easing || defaultEase) || 'easeOut',
7282                 function(){
7283                     Roo.callback(cb, this);
7284                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7285                 },
7286                 this
7287             );
7288             opt.anim = anim;
7289             return anim;
7290         },
7291
7292         // private legacy anim prep
7293         preanim : function(a, i){
7294             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7295         },
7296
7297         /**
7298          * Removes worthless text nodes
7299          * @param {Boolean} forceReclean (optional) By default the element
7300          * keeps track if it has been cleaned already so
7301          * you can call this over and over. However, if you update the element and
7302          * need to force a reclean, you can pass true.
7303          */
7304         clean : function(forceReclean){
7305             if(this.isCleaned && forceReclean !== true){
7306                 return this;
7307             }
7308             var ns = /\S/;
7309             var d = this.dom, n = d.firstChild, ni = -1;
7310             while(n){
7311                 var nx = n.nextSibling;
7312                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7313                     d.removeChild(n);
7314                 }else{
7315                     n.nodeIndex = ++ni;
7316                 }
7317                 n = nx;
7318             }
7319             this.isCleaned = true;
7320             return this;
7321         },
7322
7323         // private
7324         calcOffsetsTo : function(el){
7325             el = Roo.get(el);
7326             var d = el.dom;
7327             var restorePos = false;
7328             if(el.getStyle('position') == 'static'){
7329                 el.position('relative');
7330                 restorePos = true;
7331             }
7332             var x = 0, y =0;
7333             var op = this.dom;
7334             while(op && op != d && op.tagName != 'HTML'){
7335                 x+= op.offsetLeft;
7336                 y+= op.offsetTop;
7337                 op = op.offsetParent;
7338             }
7339             if(restorePos){
7340                 el.position('static');
7341             }
7342             return [x, y];
7343         },
7344
7345         /**
7346          * Scrolls this element into view within the passed container.
7347          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7348          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7349          * @return {Roo.Element} this
7350          */
7351         scrollIntoView : function(container, hscroll){
7352             var c = Roo.getDom(container) || document.body;
7353             var el = this.dom;
7354
7355             var o = this.calcOffsetsTo(c),
7356                 l = o[0],
7357                 t = o[1],
7358                 b = t+el.offsetHeight,
7359                 r = l+el.offsetWidth;
7360
7361             var ch = c.clientHeight;
7362             var ct = parseInt(c.scrollTop, 10);
7363             var cl = parseInt(c.scrollLeft, 10);
7364             var cb = ct + ch;
7365             var cr = cl + c.clientWidth;
7366
7367             if(t < ct){
7368                 c.scrollTop = t;
7369             }else if(b > cb){
7370                 c.scrollTop = b-ch;
7371             }
7372
7373             if(hscroll !== false){
7374                 if(l < cl){
7375                     c.scrollLeft = l;
7376                 }else if(r > cr){
7377                     c.scrollLeft = r-c.clientWidth;
7378                 }
7379             }
7380             return this;
7381         },
7382
7383         // private
7384         scrollChildIntoView : function(child, hscroll){
7385             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7386         },
7387
7388         /**
7389          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7390          * the new height may not be available immediately.
7391          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7392          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7393          * @param {Function} onComplete (optional) Function to call when animation completes
7394          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7395          * @return {Roo.Element} this
7396          */
7397         autoHeight : function(animate, duration, onComplete, easing){
7398             var oldHeight = this.getHeight();
7399             this.clip();
7400             this.setHeight(1); // force clipping
7401             setTimeout(function(){
7402                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7403                 if(!animate){
7404                     this.setHeight(height);
7405                     this.unclip();
7406                     if(typeof onComplete == "function"){
7407                         onComplete();
7408                     }
7409                 }else{
7410                     this.setHeight(oldHeight); // restore original height
7411                     this.setHeight(height, animate, duration, function(){
7412                         this.unclip();
7413                         if(typeof onComplete == "function") { onComplete(); }
7414                     }.createDelegate(this), easing);
7415                 }
7416             }.createDelegate(this), 0);
7417             return this;
7418         },
7419
7420         /**
7421          * Returns true if this element is an ancestor of the passed element
7422          * @param {HTMLElement/String} el The element to check
7423          * @return {Boolean} True if this element is an ancestor of el, else false
7424          */
7425         contains : function(el){
7426             if(!el){return false;}
7427             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7428         },
7429
7430         /**
7431          * Checks whether the element is currently visible using both visibility and display properties.
7432          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7433          * @return {Boolean} True if the element is currently visible, else false
7434          */
7435         isVisible : function(deep) {
7436             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7437             if(deep !== true || !vis){
7438                 return vis;
7439             }
7440             var p = this.dom.parentNode;
7441             while(p && p.tagName.toLowerCase() != "body"){
7442                 if(!Roo.fly(p, '_isVisible').isVisible()){
7443                     return false;
7444                 }
7445                 p = p.parentNode;
7446             }
7447             return true;
7448         },
7449
7450         /**
7451          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7454          * @return {CompositeElement/CompositeElementLite} The composite element
7455          */
7456         select : function(selector, unique){
7457             return El.select(selector, unique, this.dom);
7458         },
7459
7460         /**
7461          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7462          * @param {String} selector The CSS selector
7463          * @return {Array} An array of the matched nodes
7464          */
7465         query : function(selector, unique){
7466             return Roo.DomQuery.select(selector, this.dom);
7467         },
7468
7469         /**
7470          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7471          * @param {String} selector The CSS selector
7472          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7473          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7474          */
7475         child : function(selector, returnDom){
7476             var n = Roo.DomQuery.selectNode(selector, this.dom);
7477             return returnDom ? n : Roo.get(n);
7478         },
7479
7480         /**
7481          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7482          * @param {String} selector The CSS selector
7483          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7484          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7485          */
7486         down : function(selector, returnDom){
7487             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7488             return returnDom ? n : Roo.get(n);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7493          * @param {String} group The group the DD object is member of
7494          * @param {Object} config The DD config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DD object
7496          * @return {Roo.dd.DD} The DD object
7497          */
7498         initDD : function(group, config, overrides){
7499             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7505          * @param {String} group The group the DDProxy object is member of
7506          * @param {Object} config The DDProxy config object
7507          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7508          * @return {Roo.dd.DDProxy} The DDProxy object
7509          */
7510         initDDProxy : function(group, config, overrides){
7511             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7512             return Roo.apply(dd, overrides);
7513         },
7514
7515         /**
7516          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7517          * @param {String} group The group the DDTarget object is member of
7518          * @param {Object} config The DDTarget config object
7519          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7520          * @return {Roo.dd.DDTarget} The DDTarget object
7521          */
7522         initDDTarget : function(group, config, overrides){
7523             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7524             return Roo.apply(dd, overrides);
7525         },
7526
7527         /**
7528          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7529          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7530          * @param {Boolean} visible Whether the element is visible
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534          setVisible : function(visible, animate){
7535             if(!animate || !A){
7536                 if(this.visibilityMode == El.DISPLAY){
7537                     this.setDisplayed(visible);
7538                 }else{
7539                     this.fixDisplay();
7540                     this.dom.style.visibility = visible ? "visible" : "hidden";
7541                 }
7542             }else{
7543                 // closure for composites
7544                 var dom = this.dom;
7545                 var visMode = this.visibilityMode;
7546                 if(visible){
7547                     this.setOpacity(.01);
7548                     this.setVisible(true);
7549                 }
7550                 this.anim({opacity: { to: (visible?1:0) }},
7551                       this.preanim(arguments, 1),
7552                       null, .35, 'easeIn', function(){
7553                          if(!visible){
7554                              if(visMode == El.DISPLAY){
7555                                  dom.style.display = "none";
7556                              }else{
7557                                  dom.style.visibility = "hidden";
7558                              }
7559                              Roo.get(dom).setOpacity(1);
7560                          }
7561                      });
7562             }
7563             return this;
7564         },
7565
7566         /**
7567          * Returns true if display is not "none"
7568          * @return {Boolean}
7569          */
7570         isDisplayed : function() {
7571             return this.getStyle("display") != "none";
7572         },
7573
7574         /**
7575          * Toggles the element's visibility or display, depending on visibility mode.
7576          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7577          * @return {Roo.Element} this
7578          */
7579         toggle : function(animate){
7580             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7581             return this;
7582         },
7583
7584         /**
7585          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7586          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7587          * @return {Roo.Element} this
7588          */
7589         setDisplayed : function(value) {
7590             if(typeof value == "boolean"){
7591                value = value ? this.originalDisplay : "none";
7592             }
7593             this.setStyle("display", value);
7594             return this;
7595         },
7596
7597         /**
7598          * Tries to focus the element. Any exceptions are caught and ignored.
7599          * @return {Roo.Element} this
7600          */
7601         focus : function() {
7602             try{
7603                 this.dom.focus();
7604             }catch(e){}
7605             return this;
7606         },
7607
7608         /**
7609          * Tries to blur the element. Any exceptions are caught and ignored.
7610          * @return {Roo.Element} this
7611          */
7612         blur : function() {
7613             try{
7614                 this.dom.blur();
7615             }catch(e){}
7616             return this;
7617         },
7618
7619         /**
7620          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7621          * @param {String/Array} className The CSS class to add, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         addClass : function(className){
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.addClass(className[i]);
7628                 }
7629             }else{
7630                 if(className && !this.hasClass(className)){
7631                     this.dom.className = this.dom.className + " " + className;
7632                 }
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7639          * @param {String/Array} className The CSS class to add, or an array of classes
7640          * @return {Roo.Element} this
7641          */
7642         radioClass : function(className){
7643             var siblings = this.dom.parentNode.childNodes;
7644             for(var i = 0; i < siblings.length; i++) {
7645                 var s = siblings[i];
7646                 if(s.nodeType == 1){
7647                     Roo.get(s).removeClass(className);
7648                 }
7649             }
7650             this.addClass(className);
7651             return this;
7652         },
7653
7654         /**
7655          * Removes one or more CSS classes from the element.
7656          * @param {String/Array} className The CSS class to remove, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         removeClass : function(className){
7660             if(!className || !this.dom.className){
7661                 return this;
7662             }
7663             if(className instanceof Array){
7664                 for(var i = 0, len = className.length; i < len; i++) {
7665                     this.removeClass(className[i]);
7666                 }
7667             }else{
7668                 if(this.hasClass(className)){
7669                     var re = this.classReCache[className];
7670                     if (!re) {
7671                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7672                        this.classReCache[className] = re;
7673                     }
7674                     this.dom.className =
7675                         this.dom.className.replace(re, " ");
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         // private
7682         classReCache: {},
7683
7684         /**
7685          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7686          * @param {String} className The CSS class to toggle
7687          * @return {Roo.Element} this
7688          */
7689         toggleClass : function(className){
7690             if(this.hasClass(className)){
7691                 this.removeClass(className);
7692             }else{
7693                 this.addClass(className);
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Checks if the specified CSS class exists on this element's DOM node.
7700          * @param {String} className The CSS class to check for
7701          * @return {Boolean} True if the class exists, else false
7702          */
7703         hasClass : function(className){
7704             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7705         },
7706
7707         /**
7708          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7709          * @param {String} oldClassName The CSS class to replace
7710          * @param {String} newClassName The replacement CSS class
7711          * @return {Roo.Element} this
7712          */
7713         replaceClass : function(oldClassName, newClassName){
7714             this.removeClass(oldClassName);
7715             this.addClass(newClassName);
7716             return this;
7717         },
7718
7719         /**
7720          * Returns an object with properties matching the styles requested.
7721          * For example, el.getStyles('color', 'font-size', 'width') might return
7722          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7723          * @param {String} style1 A style name
7724          * @param {String} style2 A style name
7725          * @param {String} etc.
7726          * @return {Object} The style object
7727          */
7728         getStyles : function(){
7729             var a = arguments, len = a.length, r = {};
7730             for(var i = 0; i < len; i++){
7731                 r[a[i]] = this.getStyle(a[i]);
7732             }
7733             return r;
7734         },
7735
7736         /**
7737          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7738          * @param {String} property The style property whose value is returned.
7739          * @return {String} The current value of the style property for this element.
7740          */
7741         getStyle : function(){
7742             return view && view.getComputedStyle ?
7743                 function(prop){
7744                     var el = this.dom, v, cs, camel;
7745                     if(prop == 'float'){
7746                         prop = "cssFloat";
7747                     }
7748                     if(el.style && (v = el.style[prop])){
7749                         return v;
7750                     }
7751                     if(cs = view.getComputedStyle(el, "")){
7752                         if(!(camel = propCache[prop])){
7753                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                         }
7755                         return cs[camel];
7756                     }
7757                     return null;
7758                 } :
7759                 function(prop){
7760                     var el = this.dom, v, cs, camel;
7761                     if(prop == 'opacity'){
7762                         if(typeof el.style.filter == 'string'){
7763                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7764                             if(m){
7765                                 var fv = parseFloat(m[1]);
7766                                 if(!isNaN(fv)){
7767                                     return fv ? fv / 100 : 0;
7768                                 }
7769                             }
7770                         }
7771                         return 1;
7772                     }else if(prop == 'float'){
7773                         prop = "styleFloat";
7774                     }
7775                     if(!(camel = propCache[prop])){
7776                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7777                     }
7778                     if(v = el.style[camel]){
7779                         return v;
7780                     }
7781                     if(cs = el.currentStyle){
7782                         return cs[camel];
7783                     }
7784                     return null;
7785                 };
7786         }(),
7787
7788         /**
7789          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7790          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7791          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7792          * @return {Roo.Element} this
7793          */
7794         setStyle : function(prop, value){
7795             if(typeof prop == "string"){
7796                 
7797                 if (prop == 'float') {
7798                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7799                     return this;
7800                 }
7801                 
7802                 var camel;
7803                 if(!(camel = propCache[prop])){
7804                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7805                 }
7806                 
7807                 if(camel == 'opacity') {
7808                     this.setOpacity(value);
7809                 }else{
7810                     this.dom.style[camel] = value;
7811                 }
7812             }else{
7813                 for(var style in prop){
7814                     if(typeof prop[style] != "function"){
7815                        this.setStyle(style, prop[style]);
7816                     }
7817                 }
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * More flexible version of {@link #setStyle} for setting style properties.
7824          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7825          * a function which returns such a specification.
7826          * @return {Roo.Element} this
7827          */
7828         applyStyles : function(style){
7829             Roo.DomHelper.applyStyles(this.dom, style);
7830             return this;
7831         },
7832
7833         /**
7834           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7835           * @return {Number} The X position of the element
7836           */
7837         getX : function(){
7838             return D.getX(this.dom);
7839         },
7840
7841         /**
7842           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7843           * @return {Number} The Y position of the element
7844           */
7845         getY : function(){
7846             return D.getY(this.dom);
7847         },
7848
7849         /**
7850           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7851           * @return {Array} The XY position of the element
7852           */
7853         getXY : function(){
7854             return D.getXY(this.dom);
7855         },
7856
7857         /**
7858          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7859          * @param {Number} The X position of the element
7860          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7861          * @return {Roo.Element} this
7862          */
7863         setX : function(x, animate){
7864             if(!animate || !A){
7865                 D.setX(this.dom, x);
7866             }else{
7867                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7868             }
7869             return this;
7870         },
7871
7872         /**
7873          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7874          * @param {Number} The Y position of the element
7875          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7876          * @return {Roo.Element} this
7877          */
7878         setY : function(y, animate){
7879             if(!animate || !A){
7880                 D.setY(this.dom, y);
7881             }else{
7882                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7889          * @param {String} left The left CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setLeft : function(left){
7893             this.setStyle("left", this.addUnits(left));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7899          * @param {String} top The top CSS property value
7900          * @return {Roo.Element} this
7901          */
7902         setTop : function(top){
7903             this.setStyle("top", this.addUnits(top));
7904             return this;
7905         },
7906
7907         /**
7908          * Sets the element's CSS right style.
7909          * @param {String} right The right CSS property value
7910          * @return {Roo.Element} this
7911          */
7912         setRight : function(right){
7913             this.setStyle("right", this.addUnits(right));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the element's CSS bottom style.
7919          * @param {String} bottom The bottom CSS property value
7920          * @return {Roo.Element} this
7921          */
7922         setBottom : function(bottom){
7923             this.setStyle("bottom", this.addUnits(bottom));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         setXY : function(pos, animate){
7935             if(!animate || !A){
7936                 D.setXY(this.dom, pos);
7937             }else{
7938                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7945          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7949          * @return {Roo.Element} this
7950          */
7951         setLocation : function(x, y, animate){
7952             this.setXY([x, y], this.preanim(arguments, 2));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7958          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7962          * @return {Roo.Element} this
7963          */
7964         moveTo : function(x, y, animate){
7965             this.setXY([x, y], this.preanim(arguments, 2));
7966             return this;
7967         },
7968
7969         /**
7970          * Returns the region of the given element.
7971          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7972          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7973          */
7974         getRegion : function(){
7975             return D.getRegion(this.dom);
7976         },
7977
7978         /**
7979          * Returns the offset height of the element
7980          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7981          * @return {Number} The element's height
7982          */
7983         getHeight : function(contentHeight){
7984             var h = this.dom.offsetHeight || 0;
7985             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7986         },
7987
7988         /**
7989          * Returns the offset width of the element
7990          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7991          * @return {Number} The element's width
7992          */
7993         getWidth : function(contentWidth){
7994             var w = this.dom.offsetWidth || 0;
7995             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7996         },
7997
7998         /**
7999          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8000          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8001          * if a height has not been set using CSS.
8002          * @return {Number}
8003          */
8004         getComputedHeight : function(){
8005             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8006             if(!h){
8007                 h = parseInt(this.getStyle('height'), 10) || 0;
8008                 if(!this.isBorderBox()){
8009                     h += this.getFrameWidth('tb');
8010                 }
8011             }
8012             return h;
8013         },
8014
8015         /**
8016          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8017          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8018          * if a width has not been set using CSS.
8019          * @return {Number}
8020          */
8021         getComputedWidth : function(){
8022             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8023             if(!w){
8024                 w = parseInt(this.getStyle('width'), 10) || 0;
8025                 if(!this.isBorderBox()){
8026                     w += this.getFrameWidth('lr');
8027                 }
8028             }
8029             return w;
8030         },
8031
8032         /**
8033          * Returns the size of the element.
8034          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8035          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8036          */
8037         getSize : function(contentSize){
8038             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8039         },
8040
8041         /**
8042          * Returns the width and height of the viewport.
8043          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8044          */
8045         getViewSize : function(){
8046             var d = this.dom, doc = document, aw = 0, ah = 0;
8047             if(d == doc || d == doc.body){
8048                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8049             }else{
8050                 return {
8051                     width : d.clientWidth,
8052                     height: d.clientHeight
8053                 };
8054             }
8055         },
8056
8057         /**
8058          * Returns the value of the "value" attribute
8059          * @param {Boolean} asNumber true to parse the value as a number
8060          * @return {String/Number}
8061          */
8062         getValue : function(asNumber){
8063             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8064         },
8065
8066         // private
8067         adjustWidth : function(width){
8068             if(typeof width == "number"){
8069                 if(this.autoBoxAdjust && !this.isBorderBox()){
8070                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8071                 }
8072                 if(width < 0){
8073                     width = 0;
8074                 }
8075             }
8076             return width;
8077         },
8078
8079         // private
8080         adjustHeight : function(height){
8081             if(typeof height == "number"){
8082                if(this.autoBoxAdjust && !this.isBorderBox()){
8083                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8084                }
8085                if(height < 0){
8086                    height = 0;
8087                }
8088             }
8089             return height;
8090         },
8091
8092         /**
8093          * Set the width of the element
8094          * @param {Number} width The new width
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098         setWidth : function(width, animate){
8099             width = this.adjustWidth(width);
8100             if(!animate || !A){
8101                 this.dom.style.width = this.addUnits(width);
8102             }else{
8103                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8104             }
8105             return this;
8106         },
8107
8108         /**
8109          * Set the height of the element
8110          * @param {Number} height The new height
8111          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8112          * @return {Roo.Element} this
8113          */
8114          setHeight : function(height, animate){
8115             height = this.adjustHeight(height);
8116             if(!animate || !A){
8117                 this.dom.style.height = this.addUnits(height);
8118             }else{
8119                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131          setSize : function(width, height, animate){
8132             if(typeof width == "object"){ // in case of object from getSize()
8133                 height = width.height; width = width.width;
8134             }
8135             width = this.adjustWidth(width); height = this.adjustHeight(height);
8136             if(!animate || !A){
8137                 this.dom.style.width = this.addUnits(width);
8138                 this.dom.style.height = this.addUnits(height);
8139             }else{
8140                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8141             }
8142             return this;
8143         },
8144
8145         /**
8146          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8147          * @param {Number} x X value for new position (coordinates are page-based)
8148          * @param {Number} y Y value for new position (coordinates are page-based)
8149          * @param {Number} width The new width
8150          * @param {Number} height The new height
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154         setBounds : function(x, y, width, height, animate){
8155             if(!animate || !A){
8156                 this.setSize(width, height);
8157                 this.setLocation(x, y);
8158             }else{
8159                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8160                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8161                               this.preanim(arguments, 4), 'motion');
8162             }
8163             return this;
8164         },
8165
8166         /**
8167          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8168          * @param {Roo.lib.Region} region The region to fill
8169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8170          * @return {Roo.Element} this
8171          */
8172         setRegion : function(region, animate){
8173             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8174             return this;
8175         },
8176
8177         /**
8178          * Appends an event handler
8179          *
8180          * @param {String}   eventName     The type of event to append
8181          * @param {Function} fn        The method the event invokes
8182          * @param {Object} scope       (optional) The scope (this object) of the fn
8183          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8184          */
8185         addListener : function(eventName, fn, scope, options){
8186             if (this.dom) {
8187                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8188             }
8189         },
8190
8191         /**
8192          * Removes an event handler from this element
8193          * @param {String} eventName the type of event to remove
8194          * @param {Function} fn the method the event invokes
8195          * @return {Roo.Element} this
8196          */
8197         removeListener : function(eventName, fn){
8198             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8199             return this;
8200         },
8201
8202         /**
8203          * Removes all previous added listeners from this element
8204          * @return {Roo.Element} this
8205          */
8206         removeAllListeners : function(){
8207             E.purgeElement(this.dom);
8208             return this;
8209         },
8210
8211         relayEvent : function(eventName, observable){
8212             this.on(eventName, function(e){
8213                 observable.fireEvent(eventName, e);
8214             });
8215         },
8216
8217         /**
8218          * Set the opacity of the element
8219          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8220          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8221          * @return {Roo.Element} this
8222          */
8223          setOpacity : function(opacity, animate){
8224             if(!animate || !A){
8225                 var s = this.dom.style;
8226                 if(Roo.isIE){
8227                     s.zoom = 1;
8228                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8229                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8230                 }else{
8231                     s.opacity = opacity;
8232                 }
8233             }else{
8234                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8235             }
8236             return this;
8237         },
8238
8239         /**
8240          * Gets the left X coordinate
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getLeft : function(local){
8245             if(!local){
8246                 return this.getX();
8247             }else{
8248                 return parseInt(this.getStyle("left"), 10) || 0;
8249             }
8250         },
8251
8252         /**
8253          * Gets the right X coordinate of the element (element X position + element width)
8254          * @param {Boolean} local True to get the local css position instead of page coordinate
8255          * @return {Number}
8256          */
8257         getRight : function(local){
8258             if(!local){
8259                 return this.getX() + this.getWidth();
8260             }else{
8261                 return (this.getLeft(true) + this.getWidth()) || 0;
8262             }
8263         },
8264
8265         /**
8266          * Gets the top Y coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getTop : function(local) {
8271             if(!local){
8272                 return this.getY();
8273             }else{
8274                 return parseInt(this.getStyle("top"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the bottom Y coordinate of the element (element Y position + element height)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getBottom : function(local){
8284             if(!local){
8285                 return this.getY() + this.getHeight();
8286             }else{
8287                 return (this.getTop(true) + this.getHeight()) || 0;
8288             }
8289         },
8290
8291         /**
8292         * Initializes positioning on this element. If a desired position is not passed, it will make the
8293         * the element positioned relative IF it is not already positioned.
8294         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8295         * @param {Number} zIndex (optional) The zIndex to apply
8296         * @param {Number} x (optional) Set the page X position
8297         * @param {Number} y (optional) Set the page Y position
8298         */
8299         position : function(pos, zIndex, x, y){
8300             if(!pos){
8301                if(this.getStyle('position') == 'static'){
8302                    this.setStyle('position', 'relative');
8303                }
8304             }else{
8305                 this.setStyle("position", pos);
8306             }
8307             if(zIndex){
8308                 this.setStyle("z-index", zIndex);
8309             }
8310             if(x !== undefined && y !== undefined){
8311                 this.setXY([x, y]);
8312             }else if(x !== undefined){
8313                 this.setX(x);
8314             }else if(y !== undefined){
8315                 this.setY(y);
8316             }
8317         },
8318
8319         /**
8320         * Clear positioning back to the default when the document was loaded
8321         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8322         * @return {Roo.Element} this
8323          */
8324         clearPositioning : function(value){
8325             value = value ||'';
8326             this.setStyle({
8327                 "left": value,
8328                 "right": value,
8329                 "top": value,
8330                 "bottom": value,
8331                 "z-index": "",
8332                 "position" : "static"
8333             });
8334             return this;
8335         },
8336
8337         /**
8338         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8339         * snapshot before performing an update and then restoring the element.
8340         * @return {Object}
8341         */
8342         getPositioning : function(){
8343             var l = this.getStyle("left");
8344             var t = this.getStyle("top");
8345             return {
8346                 "position" : this.getStyle("position"),
8347                 "left" : l,
8348                 "right" : l ? "" : this.getStyle("right"),
8349                 "top" : t,
8350                 "bottom" : t ? "" : this.getStyle("bottom"),
8351                 "z-index" : this.getStyle("z-index")
8352             };
8353         },
8354
8355         /**
8356          * Gets the width of the border(s) for the specified side(s)
8357          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8358          * passing lr would get the border (l)eft width + the border (r)ight width.
8359          * @return {Number} The width of the sides passed added together
8360          */
8361         getBorderWidth : function(side){
8362             return this.addStyles(side, El.borders);
8363         },
8364
8365         /**
8366          * Gets the width of the padding(s) for the specified side(s)
8367          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8368          * passing lr would get the padding (l)eft + the padding (r)ight.
8369          * @return {Number} The padding of the sides passed added together
8370          */
8371         getPadding : function(side){
8372             return this.addStyles(side, El.paddings);
8373         },
8374
8375         /**
8376         * Set positioning with an object returned by getPositioning().
8377         * @param {Object} posCfg
8378         * @return {Roo.Element} this
8379          */
8380         setPositioning : function(pc){
8381             this.applyStyles(pc);
8382             if(pc.right == "auto"){
8383                 this.dom.style.right = "";
8384             }
8385             if(pc.bottom == "auto"){
8386                 this.dom.style.bottom = "";
8387             }
8388             return this;
8389         },
8390
8391         // private
8392         fixDisplay : function(){
8393             if(this.getStyle("display") == "none"){
8394                 this.setStyle("visibility", "hidden");
8395                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8396                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8397                     this.setStyle("display", "block");
8398                 }
8399             }
8400         },
8401
8402         /**
8403          * Quick set left and top adding default units
8404          * @param {String} left The left CSS property value
8405          * @param {String} top The top CSS property value
8406          * @return {Roo.Element} this
8407          */
8408          setLeftTop : function(left, top){
8409             this.dom.style.left = this.addUnits(left);
8410             this.dom.style.top = this.addUnits(top);
8411             return this;
8412         },
8413
8414         /**
8415          * Move this element relative to its current position.
8416          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8417          * @param {Number} distance How far to move the element in pixels
8418          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8419          * @return {Roo.Element} this
8420          */
8421          move : function(direction, distance, animate){
8422             var xy = this.getXY();
8423             direction = direction.toLowerCase();
8424             switch(direction){
8425                 case "l":
8426                 case "left":
8427                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8428                     break;
8429                case "r":
8430                case "right":
8431                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8432                     break;
8433                case "t":
8434                case "top":
8435                case "up":
8436                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8437                     break;
8438                case "b":
8439                case "bottom":
8440                case "down":
8441                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8442                     break;
8443             }
8444             return this;
8445         },
8446
8447         /**
8448          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8449          * @return {Roo.Element} this
8450          */
8451         clip : function(){
8452             if(!this.isClipped){
8453                this.isClipped = true;
8454                this.originalClip = {
8455                    "o": this.getStyle("overflow"),
8456                    "x": this.getStyle("overflow-x"),
8457                    "y": this.getStyle("overflow-y")
8458                };
8459                this.setStyle("overflow", "hidden");
8460                this.setStyle("overflow-x", "hidden");
8461                this.setStyle("overflow-y", "hidden");
8462             }
8463             return this;
8464         },
8465
8466         /**
8467          *  Return clipping (overflow) to original clipping before clip() was called
8468          * @return {Roo.Element} this
8469          */
8470         unclip : function(){
8471             if(this.isClipped){
8472                 this.isClipped = false;
8473                 var o = this.originalClip;
8474                 if(o.o){this.setStyle("overflow", o.o);}
8475                 if(o.x){this.setStyle("overflow-x", o.x);}
8476                 if(o.y){this.setStyle("overflow-y", o.y);}
8477             }
8478             return this;
8479         },
8480
8481
8482         /**
8483          * Gets the x,y coordinates specified by the anchor position on the element.
8484          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8485          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8486          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8487          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8488          * @return {Array} [x, y] An array containing the element's x and y coordinates
8489          */
8490         getAnchorXY : function(anchor, local, s){
8491             //Passing a different size is useful for pre-calculating anchors,
8492             //especially for anchored animations that change the el size.
8493
8494             var w, h, vp = false;
8495             if(!s){
8496                 var d = this.dom;
8497                 if(d == document.body || d == document){
8498                     vp = true;
8499                     w = D.getViewWidth(); h = D.getViewHeight();
8500                 }else{
8501                     w = this.getWidth(); h = this.getHeight();
8502                 }
8503             }else{
8504                 w = s.width;  h = s.height;
8505             }
8506             var x = 0, y = 0, r = Math.round;
8507             switch((anchor || "tl").toLowerCase()){
8508                 case "c":
8509                     x = r(w*.5);
8510                     y = r(h*.5);
8511                 break;
8512                 case "t":
8513                     x = r(w*.5);
8514                     y = 0;
8515                 break;
8516                 case "l":
8517                     x = 0;
8518                     y = r(h*.5);
8519                 break;
8520                 case "r":
8521                     x = w;
8522                     y = r(h*.5);
8523                 break;
8524                 case "b":
8525                     x = r(w*.5);
8526                     y = h;
8527                 break;
8528                 case "tl":
8529                     x = 0;
8530                     y = 0;
8531                 break;
8532                 case "bl":
8533                     x = 0;
8534                     y = h;
8535                 break;
8536                 case "br":
8537                     x = w;
8538                     y = h;
8539                 break;
8540                 case "tr":
8541                     x = w;
8542                     y = 0;
8543                 break;
8544             }
8545             if(local === true){
8546                 return [x, y];
8547             }
8548             if(vp){
8549                 var sc = this.getScroll();
8550                 return [x + sc.left, y + sc.top];
8551             }
8552             //Add the element's offset xy
8553             var o = this.getXY();
8554             return [x+o[0], y+o[1]];
8555         },
8556
8557         /**
8558          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8559          * supported position values.
8560          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8561          * @param {String} position The position to align to.
8562          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8563          * @return {Array} [x, y]
8564          */
8565         getAlignToXY : function(el, p, o){
8566             el = Roo.get(el);
8567             var d = this.dom;
8568             if(!el.dom){
8569                 throw "Element.alignTo with an element that doesn't exist";
8570             }
8571             var c = false; //constrain to viewport
8572             var p1 = "", p2 = "";
8573             o = o || [0,0];
8574
8575             if(!p){
8576                 p = "tl-bl";
8577             }else if(p == "?"){
8578                 p = "tl-bl?";
8579             }else if(p.indexOf("-") == -1){
8580                 p = "tl-" + p;
8581             }
8582             p = p.toLowerCase();
8583             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8584             if(!m){
8585                throw "Element.alignTo with an invalid alignment " + p;
8586             }
8587             p1 = m[1]; p2 = m[2]; c = !!m[3];
8588
8589             //Subtract the aligned el's internal xy from the target's offset xy
8590             //plus custom offset to get the aligned el's new offset xy
8591             var a1 = this.getAnchorXY(p1, true);
8592             var a2 = el.getAnchorXY(p2, false);
8593             var x = a2[0] - a1[0] + o[0];
8594             var y = a2[1] - a1[1] + o[1];
8595             if(c){
8596                 //constrain the aligned el to viewport if necessary
8597                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8598                 // 5px of margin for ie
8599                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8600
8601                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8602                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8603                 //otherwise swap the aligned el to the opposite border of the target.
8604                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8605                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8606                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8607                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8608
8609                var doc = document;
8610                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8611                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8612
8613                if((x+w) > dw + scrollX){
8614                     x = swapX ? r.left-w : dw+scrollX-w;
8615                 }
8616                if(x < scrollX){
8617                    x = swapX ? r.right : scrollX;
8618                }
8619                if((y+h) > dh + scrollY){
8620                     y = swapY ? r.top-h : dh+scrollY-h;
8621                 }
8622                if (y < scrollY){
8623                    y = swapY ? r.bottom : scrollY;
8624                }
8625             }
8626             return [x,y];
8627         },
8628
8629         // private
8630         getConstrainToXY : function(){
8631             var os = {top:0, left:0, bottom:0, right: 0};
8632
8633             return function(el, local, offsets, proposedXY){
8634                 el = Roo.get(el);
8635                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8636
8637                 var vw, vh, vx = 0, vy = 0;
8638                 if(el.dom == document.body || el.dom == document){
8639                     vw = Roo.lib.Dom.getViewWidth();
8640                     vh = Roo.lib.Dom.getViewHeight();
8641                 }else{
8642                     vw = el.dom.clientWidth;
8643                     vh = el.dom.clientHeight;
8644                     if(!local){
8645                         var vxy = el.getXY();
8646                         vx = vxy[0];
8647                         vy = vxy[1];
8648                     }
8649                 }
8650
8651                 var s = el.getScroll();
8652
8653                 vx += offsets.left + s.left;
8654                 vy += offsets.top + s.top;
8655
8656                 vw -= offsets.right;
8657                 vh -= offsets.bottom;
8658
8659                 var vr = vx+vw;
8660                 var vb = vy+vh;
8661
8662                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8663                 var x = xy[0], y = xy[1];
8664                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8665
8666                 // only move it if it needs it
8667                 var moved = false;
8668
8669                 // first validate right/bottom
8670                 if((x + w) > vr){
8671                     x = vr - w;
8672                     moved = true;
8673                 }
8674                 if((y + h) > vb){
8675                     y = vb - h;
8676                     moved = true;
8677                 }
8678                 // then make sure top/left isn't negative
8679                 if(x < vx){
8680                     x = vx;
8681                     moved = true;
8682                 }
8683                 if(y < vy){
8684                     y = vy;
8685                     moved = true;
8686                 }
8687                 return moved ? [x, y] : false;
8688             };
8689         }(),
8690
8691         // private
8692         adjustForConstraints : function(xy, parent, offsets){
8693             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8694         },
8695
8696         /**
8697          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8698          * document it aligns it to the viewport.
8699          * The position parameter is optional, and can be specified in any one of the following formats:
8700          * <ul>
8701          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8702          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8703          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8704          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8705          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8706          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8707          * </ul>
8708          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8709          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8710          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8711          * that specified in order to enforce the viewport constraints.
8712          * Following are all of the supported anchor positions:
8713     <pre>
8714     Value  Description
8715     -----  -----------------------------
8716     tl     The top left corner (default)
8717     t      The center of the top edge
8718     tr     The top right corner
8719     l      The center of the left edge
8720     c      In the center of the element
8721     r      The center of the right edge
8722     bl     The bottom left corner
8723     b      The center of the bottom edge
8724     br     The bottom right corner
8725     </pre>
8726     Example Usage:
8727     <pre><code>
8728     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8729     el.alignTo("other-el");
8730
8731     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8732     el.alignTo("other-el", "tr?");
8733
8734     // align the bottom right corner of el with the center left edge of other-el
8735     el.alignTo("other-el", "br-l?");
8736
8737     // align the center of el with the bottom left corner of other-el and
8738     // adjust the x position by -6 pixels (and the y position by 0)
8739     el.alignTo("other-el", "c-bl", [-6, 0]);
8740     </code></pre>
8741          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8742          * @param {String} position The position to align to.
8743          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8745          * @return {Roo.Element} this
8746          */
8747         alignTo : function(element, position, offsets, animate){
8748             var xy = this.getAlignToXY(element, position, offsets);
8749             this.setXY(xy, this.preanim(arguments, 3));
8750             return this;
8751         },
8752
8753         /**
8754          * Anchors an element to another element and realigns it when the window is resized.
8755          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8756          * @param {String} position The position to align to.
8757          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8758          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8759          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8760          * is a number, it is used as the buffer delay (defaults to 50ms).
8761          * @param {Function} callback The function to call after the animation finishes
8762          * @return {Roo.Element} this
8763          */
8764         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8765             var action = function(){
8766                 this.alignTo(el, alignment, offsets, animate);
8767                 Roo.callback(callback, this);
8768             };
8769             Roo.EventManager.onWindowResize(action, this);
8770             var tm = typeof monitorScroll;
8771             if(tm != 'undefined'){
8772                 Roo.EventManager.on(window, 'scroll', action, this,
8773                     {buffer: tm == 'number' ? monitorScroll : 50});
8774             }
8775             action.call(this); // align immediately
8776             return this;
8777         },
8778         /**
8779          * Clears any opacity settings from this element. Required in some cases for IE.
8780          * @return {Roo.Element} this
8781          */
8782         clearOpacity : function(){
8783             if (window.ActiveXObject) {
8784                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8785                     this.dom.style.filter = "";
8786                 }
8787             } else {
8788                 this.dom.style.opacity = "";
8789                 this.dom.style["-moz-opacity"] = "";
8790                 this.dom.style["-khtml-opacity"] = "";
8791             }
8792             return this;
8793         },
8794
8795         /**
8796          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8797          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8798          * @return {Roo.Element} this
8799          */
8800         hide : function(animate){
8801             this.setVisible(false, this.preanim(arguments, 0));
8802             return this;
8803         },
8804
8805         /**
8806         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8807         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8808          * @return {Roo.Element} this
8809          */
8810         show : function(animate){
8811             this.setVisible(true, this.preanim(arguments, 0));
8812             return this;
8813         },
8814
8815         /**
8816          * @private Test if size has a unit, otherwise appends the default
8817          */
8818         addUnits : function(size){
8819             return Roo.Element.addUnits(size, this.defaultUnit);
8820         },
8821
8822         /**
8823          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8824          * @return {Roo.Element} this
8825          */
8826         beginMeasure : function(){
8827             var el = this.dom;
8828             if(el.offsetWidth || el.offsetHeight){
8829                 return this; // offsets work already
8830             }
8831             var changed = [];
8832             var p = this.dom, b = document.body; // start with this element
8833             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8834                 var pe = Roo.get(p);
8835                 if(pe.getStyle('display') == 'none'){
8836                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8837                     p.style.visibility = "hidden";
8838                     p.style.display = "block";
8839                 }
8840                 p = p.parentNode;
8841             }
8842             this._measureChanged = changed;
8843             return this;
8844
8845         },
8846
8847         /**
8848          * Restores displays to before beginMeasure was called
8849          * @return {Roo.Element} this
8850          */
8851         endMeasure : function(){
8852             var changed = this._measureChanged;
8853             if(changed){
8854                 for(var i = 0, len = changed.length; i < len; i++) {
8855                     var r = changed[i];
8856                     r.el.style.visibility = r.visibility;
8857                     r.el.style.display = "none";
8858                 }
8859                 this._measureChanged = null;
8860             }
8861             return this;
8862         },
8863
8864         /**
8865         * Update the innerHTML of this element, optionally searching for and processing scripts
8866         * @param {String} html The new HTML
8867         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8868         * @param {Function} callback For async script loading you can be noticed when the update completes
8869         * @return {Roo.Element} this
8870          */
8871         update : function(html, loadScripts, callback){
8872             if(typeof html == "undefined"){
8873                 html = "";
8874             }
8875             if(loadScripts !== true){
8876                 this.dom.innerHTML = html;
8877                 if(typeof callback == "function"){
8878                     callback();
8879                 }
8880                 return this;
8881             }
8882             var id = Roo.id();
8883             var dom = this.dom;
8884
8885             html += '<span id="' + id + '"></span>';
8886
8887             E.onAvailable(id, function(){
8888                 var hd = document.getElementsByTagName("head")[0];
8889                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8890                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8891                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8892
8893                 var match;
8894                 while(match = re.exec(html)){
8895                     var attrs = match[1];
8896                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8897                     if(srcMatch && srcMatch[2]){
8898                        var s = document.createElement("script");
8899                        s.src = srcMatch[2];
8900                        var typeMatch = attrs.match(typeRe);
8901                        if(typeMatch && typeMatch[2]){
8902                            s.type = typeMatch[2];
8903                        }
8904                        hd.appendChild(s);
8905                     }else if(match[2] && match[2].length > 0){
8906                         if(window.execScript) {
8907                            window.execScript(match[2]);
8908                         } else {
8909                             /**
8910                              * eval:var:id
8911                              * eval:var:dom
8912                              * eval:var:html
8913                              * 
8914                              */
8915                            window.eval(match[2]);
8916                         }
8917                     }
8918                 }
8919                 var el = document.getElementById(id);
8920                 if(el){el.parentNode.removeChild(el);}
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924             });
8925             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8926             return this;
8927         },
8928
8929         /**
8930          * Direct access to the UpdateManager update() method (takes the same parameters).
8931          * @param {String/Function} url The url for this request or a function to call to get the url
8932          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8933          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8934          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8935          * @return {Roo.Element} this
8936          */
8937         load : function(){
8938             var um = this.getUpdateManager();
8939             um.update.apply(um, arguments);
8940             return this;
8941         },
8942
8943         /**
8944         * Gets this element's UpdateManager
8945         * @return {Roo.UpdateManager} The UpdateManager
8946         */
8947         getUpdateManager : function(){
8948             if(!this.updateManager){
8949                 this.updateManager = new Roo.UpdateManager(this);
8950             }
8951             return this.updateManager;
8952         },
8953
8954         /**
8955          * Disables text selection for this element (normalized across browsers)
8956          * @return {Roo.Element} this
8957          */
8958         unselectable : function(){
8959             this.dom.unselectable = "on";
8960             this.swallowEvent("selectstart", true);
8961             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8962             this.addClass("x-unselectable");
8963             return this;
8964         },
8965
8966         /**
8967         * Calculates the x, y to center this element on the screen
8968         * @return {Array} The x, y values [x, y]
8969         */
8970         getCenterXY : function(){
8971             return this.getAlignToXY(document, 'c-c');
8972         },
8973
8974         /**
8975         * Centers the Element in either the viewport, or another Element.
8976         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8977         */
8978         center : function(centerIn){
8979             this.alignTo(centerIn || document, 'c-c');
8980             return this;
8981         },
8982
8983         /**
8984          * Tests various css rules/browsers to determine if this element uses a border box
8985          * @return {Boolean}
8986          */
8987         isBorderBox : function(){
8988             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8989         },
8990
8991         /**
8992          * Return a box {x, y, width, height} that can be used to set another elements
8993          * size/location to match this element.
8994          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8995          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8996          * @return {Object} box An object in the format {x, y, width, height}
8997          */
8998         getBox : function(contentBox, local){
8999             var xy;
9000             if(!local){
9001                 xy = this.getXY();
9002             }else{
9003                 var left = parseInt(this.getStyle("left"), 10) || 0;
9004                 var top = parseInt(this.getStyle("top"), 10) || 0;
9005                 xy = [left, top];
9006             }
9007             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9008             if(!contentBox){
9009                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9010             }else{
9011                 var l = this.getBorderWidth("l")+this.getPadding("l");
9012                 var r = this.getBorderWidth("r")+this.getPadding("r");
9013                 var t = this.getBorderWidth("t")+this.getPadding("t");
9014                 var b = this.getBorderWidth("b")+this.getPadding("b");
9015                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9016             }
9017             bx.right = bx.x + bx.width;
9018             bx.bottom = bx.y + bx.height;
9019             return bx;
9020         },
9021
9022         /**
9023          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9024          for more information about the sides.
9025          * @param {String} sides
9026          * @return {Number}
9027          */
9028         getFrameWidth : function(sides, onlyContentBox){
9029             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9030         },
9031
9032         /**
9033          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9034          * @param {Object} box The box to fill {x, y, width, height}
9035          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9036          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9037          * @return {Roo.Element} this
9038          */
9039         setBox : function(box, adjust, animate){
9040             var w = box.width, h = box.height;
9041             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9042                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9043                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9044             }
9045             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9046             return this;
9047         },
9048
9049         /**
9050          * Forces the browser to repaint this element
9051          * @return {Roo.Element} this
9052          */
9053          repaint : function(){
9054             var dom = this.dom;
9055             this.addClass("x-repaint");
9056             setTimeout(function(){
9057                 Roo.get(dom).removeClass("x-repaint");
9058             }, 1);
9059             return this;
9060         },
9061
9062         /**
9063          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9064          * then it returns the calculated width of the sides (see getPadding)
9065          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9066          * @return {Object/Number}
9067          */
9068         getMargins : function(side){
9069             if(!side){
9070                 return {
9071                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9072                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9073                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9074                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9075                 };
9076             }else{
9077                 return this.addStyles(side, El.margins);
9078              }
9079         },
9080
9081         // private
9082         addStyles : function(sides, styles){
9083             var val = 0, v, w;
9084             for(var i = 0, len = sides.length; i < len; i++){
9085                 v = this.getStyle(styles[sides.charAt(i)]);
9086                 if(v){
9087                      w = parseInt(v, 10);
9088                      if(w){ val += w; }
9089                 }
9090             }
9091             return val;
9092         },
9093
9094         /**
9095          * Creates a proxy element of this element
9096          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9097          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9098          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9099          * @return {Roo.Element} The new proxy element
9100          */
9101         createProxy : function(config, renderTo, matchBox){
9102             if(renderTo){
9103                 renderTo = Roo.getDom(renderTo);
9104             }else{
9105                 renderTo = document.body;
9106             }
9107             config = typeof config == "object" ?
9108                 config : {tag : "div", cls: config};
9109             var proxy = Roo.DomHelper.append(renderTo, config, true);
9110             if(matchBox){
9111                proxy.setBox(this.getBox());
9112             }
9113             return proxy;
9114         },
9115
9116         /**
9117          * Puts a mask over this element to disable user interaction. Requires core.css.
9118          * This method can only be applied to elements which accept child nodes.
9119          * @param {String} msg (optional) A message to display in the mask
9120          * @param {String} msgCls (optional) A css class to apply to the msg element
9121          * @return {Element} The mask  element
9122          */
9123         mask : function(msg, msgCls)
9124         {
9125             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9126                 this.setStyle("position", "relative");
9127             }
9128             if(!this._mask){
9129                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9130             }
9131             
9132             this.addClass("x-masked");
9133             this._mask.setDisplayed(true);
9134             
9135             // we wander
9136             var z = 0;
9137             var dom = this.dom;
9138             while (dom && dom.style) {
9139                 if (!isNaN(parseInt(dom.style.zIndex))) {
9140                     z = Math.max(z, parseInt(dom.style.zIndex));
9141                 }
9142                 dom = dom.parentNode;
9143             }
9144             // if we are masking the body - then it hides everything..
9145             if (this.dom == document.body) {
9146                 z = 1000000;
9147                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9148                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9149             }
9150            
9151             if(typeof msg == 'string'){
9152                 if(!this._maskMsg){
9153                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9154                         cls: "roo-el-mask-msg", 
9155                         cn: [
9156                             {
9157                                 tag: 'i',
9158                                 cls: 'fa fa-spinner fa-spin'
9159                             },
9160                             {
9161                                 tag: 'div'
9162                             }   
9163                         ]
9164                     }, true);
9165                 }
9166                 var mm = this._maskMsg;
9167                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9168                 if (mm.dom.lastChild) { // weird IE issue?
9169                     mm.dom.lastChild.innerHTML = msg;
9170                 }
9171                 mm.setDisplayed(true);
9172                 mm.center(this);
9173                 mm.setStyle('z-index', z + 102);
9174             }
9175             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9176                 this._mask.setHeight(this.getHeight());
9177             }
9178             this._mask.setStyle('z-index', z + 100);
9179             
9180             return this._mask;
9181         },
9182
9183         /**
9184          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9185          * it is cached for reuse.
9186          */
9187         unmask : function(removeEl){
9188             if(this._mask){
9189                 if(removeEl === true){
9190                     this._mask.remove();
9191                     delete this._mask;
9192                     if(this._maskMsg){
9193                         this._maskMsg.remove();
9194                         delete this._maskMsg;
9195                     }
9196                 }else{
9197                     this._mask.setDisplayed(false);
9198                     if(this._maskMsg){
9199                         this._maskMsg.setDisplayed(false);
9200                     }
9201                 }
9202             }
9203             this.removeClass("x-masked");
9204         },
9205
9206         /**
9207          * Returns true if this element is masked
9208          * @return {Boolean}
9209          */
9210         isMasked : function(){
9211             return this._mask && this._mask.isVisible();
9212         },
9213
9214         /**
9215          * Creates an iframe shim for this element to keep selects and other windowed objects from
9216          * showing through.
9217          * @return {Roo.Element} The new shim element
9218          */
9219         createShim : function(){
9220             var el = document.createElement('iframe');
9221             el.frameBorder = 'no';
9222             el.className = 'roo-shim';
9223             if(Roo.isIE && Roo.isSecure){
9224                 el.src = Roo.SSL_SECURE_URL;
9225             }
9226             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9227             shim.autoBoxAdjust = false;
9228             return shim;
9229         },
9230
9231         /**
9232          * Removes this element from the DOM and deletes it from the cache
9233          */
9234         remove : function(){
9235             if(this.dom.parentNode){
9236                 this.dom.parentNode.removeChild(this.dom);
9237             }
9238             delete El.cache[this.dom.id];
9239         },
9240
9241         /**
9242          * Sets up event handlers to add and remove a css class when the mouse is over this element
9243          * @param {String} className
9244          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9245          * mouseout events for children elements
9246          * @return {Roo.Element} this
9247          */
9248         addClassOnOver : function(className, preventFlicker){
9249             this.on("mouseover", function(){
9250                 Roo.fly(this, '_internal').addClass(className);
9251             }, this.dom);
9252             var removeFn = function(e){
9253                 if(preventFlicker !== true || !e.within(this, true)){
9254                     Roo.fly(this, '_internal').removeClass(className);
9255                 }
9256             };
9257             this.on("mouseout", removeFn, this.dom);
9258             return this;
9259         },
9260
9261         /**
9262          * Sets up event handlers to add and remove a css class when this element has the focus
9263          * @param {String} className
9264          * @return {Roo.Element} this
9265          */
9266         addClassOnFocus : function(className){
9267             this.on("focus", function(){
9268                 Roo.fly(this, '_internal').addClass(className);
9269             }, this.dom);
9270             this.on("blur", function(){
9271                 Roo.fly(this, '_internal').removeClass(className);
9272             }, this.dom);
9273             return this;
9274         },
9275         /**
9276          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9277          * @param {String} className
9278          * @return {Roo.Element} this
9279          */
9280         addClassOnClick : function(className){
9281             var dom = this.dom;
9282             this.on("mousedown", function(){
9283                 Roo.fly(dom, '_internal').addClass(className);
9284                 var d = Roo.get(document);
9285                 var fn = function(){
9286                     Roo.fly(dom, '_internal').removeClass(className);
9287                     d.removeListener("mouseup", fn);
9288                 };
9289                 d.on("mouseup", fn);
9290             });
9291             return this;
9292         },
9293
9294         /**
9295          * Stops the specified event from bubbling and optionally prevents the default action
9296          * @param {String} eventName
9297          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9298          * @return {Roo.Element} this
9299          */
9300         swallowEvent : function(eventName, preventDefault){
9301             var fn = function(e){
9302                 e.stopPropagation();
9303                 if(preventDefault){
9304                     e.preventDefault();
9305                 }
9306             };
9307             if(eventName instanceof Array){
9308                 for(var i = 0, len = eventName.length; i < len; i++){
9309                      this.on(eventName[i], fn);
9310                 }
9311                 return this;
9312             }
9313             this.on(eventName, fn);
9314             return this;
9315         },
9316
9317         /**
9318          * @private
9319          */
9320       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9321
9322         /**
9323          * Sizes this element to its parent element's dimensions performing
9324          * neccessary box adjustments.
9325          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9326          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9327          * @return {Roo.Element} this
9328          */
9329         fitToParent : function(monitorResize, targetParent) {
9330           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9331           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9332           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9333             return;
9334           }
9335           var p = Roo.get(targetParent || this.dom.parentNode);
9336           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9337           if (monitorResize === true) {
9338             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9339             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9340           }
9341           return this;
9342         },
9343
9344         /**
9345          * Gets the next sibling, skipping text nodes
9346          * @return {HTMLElement} The next sibling or null
9347          */
9348         getNextSibling : function(){
9349             var n = this.dom.nextSibling;
9350             while(n && n.nodeType != 1){
9351                 n = n.nextSibling;
9352             }
9353             return n;
9354         },
9355
9356         /**
9357          * Gets the previous sibling, skipping text nodes
9358          * @return {HTMLElement} The previous sibling or null
9359          */
9360         getPrevSibling : function(){
9361             var n = this.dom.previousSibling;
9362             while(n && n.nodeType != 1){
9363                 n = n.previousSibling;
9364             }
9365             return n;
9366         },
9367
9368
9369         /**
9370          * Appends the passed element(s) to this element
9371          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9372          * @return {Roo.Element} this
9373          */
9374         appendChild: function(el){
9375             el = Roo.get(el);
9376             el.appendTo(this);
9377             return this;
9378         },
9379
9380         /**
9381          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9382          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9383          * automatically generated with the specified attributes.
9384          * @param {HTMLElement} insertBefore (optional) a child element of this element
9385          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9386          * @return {Roo.Element} The new child element
9387          */
9388         createChild: function(config, insertBefore, returnDom){
9389             config = config || {tag:'div'};
9390             if(insertBefore){
9391                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9392             }
9393             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9394         },
9395
9396         /**
9397          * Appends this element to the passed element
9398          * @param {String/HTMLElement/Element} el The new parent element
9399          * @return {Roo.Element} this
9400          */
9401         appendTo: function(el){
9402             el = Roo.getDom(el);
9403             el.appendChild(this.dom);
9404             return this;
9405         },
9406
9407         /**
9408          * Inserts this element before the passed element in the DOM
9409          * @param {String/HTMLElement/Element} el The element to insert before
9410          * @return {Roo.Element} this
9411          */
9412         insertBefore: function(el){
9413             el = Roo.getDom(el);
9414             el.parentNode.insertBefore(this.dom, el);
9415             return this;
9416         },
9417
9418         /**
9419          * Inserts this element after the passed element in the DOM
9420          * @param {String/HTMLElement/Element} el The element to insert after
9421          * @return {Roo.Element} this
9422          */
9423         insertAfter: function(el){
9424             el = Roo.getDom(el);
9425             el.parentNode.insertBefore(this.dom, el.nextSibling);
9426             return this;
9427         },
9428
9429         /**
9430          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9431          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9432          * @return {Roo.Element} The new child
9433          */
9434         insertFirst: function(el, returnDom){
9435             el = el || {};
9436             if(typeof el == 'object' && !el.nodeType){ // dh config
9437                 return this.createChild(el, this.dom.firstChild, returnDom);
9438             }else{
9439                 el = Roo.getDom(el);
9440                 this.dom.insertBefore(el, this.dom.firstChild);
9441                 return !returnDom ? Roo.get(el) : el;
9442             }
9443         },
9444
9445         /**
9446          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9447          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9448          * @param {String} where (optional) 'before' or 'after' defaults to before
9449          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9450          * @return {Roo.Element} the inserted Element
9451          */
9452         insertSibling: function(el, where, returnDom){
9453             where = where ? where.toLowerCase() : 'before';
9454             el = el || {};
9455             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9456
9457             if(typeof el == 'object' && !el.nodeType){ // dh config
9458                 if(where == 'after' && !this.dom.nextSibling){
9459                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9460                 }else{
9461                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9462                 }
9463
9464             }else{
9465                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9466                             where == 'before' ? this.dom : this.dom.nextSibling);
9467                 if(!returnDom){
9468                     rt = Roo.get(rt);
9469                 }
9470             }
9471             return rt;
9472         },
9473
9474         /**
9475          * Creates and wraps this element with another element
9476          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9477          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9478          * @return {HTMLElement/Element} The newly created wrapper element
9479          */
9480         wrap: function(config, returnDom){
9481             if(!config){
9482                 config = {tag: "div"};
9483             }
9484             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9485             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9486             return newEl;
9487         },
9488
9489         /**
9490          * Replaces the passed element with this element
9491          * @param {String/HTMLElement/Element} el The element to replace
9492          * @return {Roo.Element} this
9493          */
9494         replace: function(el){
9495             el = Roo.get(el);
9496             this.insertBefore(el);
9497             el.remove();
9498             return this;
9499         },
9500
9501         /**
9502          * Inserts an html fragment into this element
9503          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9504          * @param {String} html The HTML fragment
9505          * @param {Boolean} returnEl True to return an Roo.Element
9506          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9507          */
9508         insertHtml : function(where, html, returnEl){
9509             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9510             return returnEl ? Roo.get(el) : el;
9511         },
9512
9513         /**
9514          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9515          * @param {Object} o The object with the attributes
9516          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9517          * @return {Roo.Element} this
9518          */
9519         set : function(o, useSet){
9520             var el = this.dom;
9521             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9522             for(var attr in o){
9523                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9524                 if(attr=="cls"){
9525                     el.className = o["cls"];
9526                 }else{
9527                     if(useSet) {
9528                         el.setAttribute(attr, o[attr]);
9529                     } else {
9530                         el[attr] = o[attr];
9531                     }
9532                 }
9533             }
9534             if(o.style){
9535                 Roo.DomHelper.applyStyles(el, o.style);
9536             }
9537             return this;
9538         },
9539
9540         /**
9541          * Convenience method for constructing a KeyMap
9542          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9543          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9544          * @param {Function} fn The function to call
9545          * @param {Object} scope (optional) The scope of the function
9546          * @return {Roo.KeyMap} The KeyMap created
9547          */
9548         addKeyListener : function(key, fn, scope){
9549             var config;
9550             if(typeof key != "object" || key instanceof Array){
9551                 config = {
9552                     key: key,
9553                     fn: fn,
9554                     scope: scope
9555                 };
9556             }else{
9557                 config = {
9558                     key : key.key,
9559                     shift : key.shift,
9560                     ctrl : key.ctrl,
9561                     alt : key.alt,
9562                     fn: fn,
9563                     scope: scope
9564                 };
9565             }
9566             return new Roo.KeyMap(this, config);
9567         },
9568
9569         /**
9570          * Creates a KeyMap for this element
9571          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9572          * @return {Roo.KeyMap} The KeyMap created
9573          */
9574         addKeyMap : function(config){
9575             return new Roo.KeyMap(this, config);
9576         },
9577
9578         /**
9579          * Returns true if this element is scrollable.
9580          * @return {Boolean}
9581          */
9582          isScrollable : function(){
9583             var dom = this.dom;
9584             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9585         },
9586
9587         /**
9588          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9589          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9590          * @param {Number} value The new scroll value
9591          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9592          * @return {Element} this
9593          */
9594
9595         scrollTo : function(side, value, animate){
9596             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9597             if(!animate || !A){
9598                 this.dom[prop] = value;
9599             }else{
9600                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9601                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9602             }
9603             return this;
9604         },
9605
9606         /**
9607          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9608          * within this element's scrollable range.
9609          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9610          * @param {Number} distance How far to scroll the element in pixels
9611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9612          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9613          * was scrolled as far as it could go.
9614          */
9615          scroll : function(direction, distance, animate){
9616              if(!this.isScrollable()){
9617                  return;
9618              }
9619              var el = this.dom;
9620              var l = el.scrollLeft, t = el.scrollTop;
9621              var w = el.scrollWidth, h = el.scrollHeight;
9622              var cw = el.clientWidth, ch = el.clientHeight;
9623              direction = direction.toLowerCase();
9624              var scrolled = false;
9625              var a = this.preanim(arguments, 2);
9626              switch(direction){
9627                  case "l":
9628                  case "left":
9629                      if(w - l > cw){
9630                          var v = Math.min(l + distance, w-cw);
9631                          this.scrollTo("left", v, a);
9632                          scrolled = true;
9633                      }
9634                      break;
9635                 case "r":
9636                 case "right":
9637                      if(l > 0){
9638                          var v = Math.max(l - distance, 0);
9639                          this.scrollTo("left", v, a);
9640                          scrolled = true;
9641                      }
9642                      break;
9643                 case "t":
9644                 case "top":
9645                 case "up":
9646                      if(t > 0){
9647                          var v = Math.max(t - distance, 0);
9648                          this.scrollTo("top", v, a);
9649                          scrolled = true;
9650                      }
9651                      break;
9652                 case "b":
9653                 case "bottom":
9654                 case "down":
9655                      if(h - t > ch){
9656                          var v = Math.min(t + distance, h-ch);
9657                          this.scrollTo("top", v, a);
9658                          scrolled = true;
9659                      }
9660                      break;
9661              }
9662              return scrolled;
9663         },
9664
9665         /**
9666          * Translates the passed page coordinates into left/top css values for this element
9667          * @param {Number/Array} x The page x or an array containing [x, y]
9668          * @param {Number} y The page y
9669          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9670          */
9671         translatePoints : function(x, y){
9672             if(typeof x == 'object' || x instanceof Array){
9673                 y = x[1]; x = x[0];
9674             }
9675             var p = this.getStyle('position');
9676             var o = this.getXY();
9677
9678             var l = parseInt(this.getStyle('left'), 10);
9679             var t = parseInt(this.getStyle('top'), 10);
9680
9681             if(isNaN(l)){
9682                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9683             }
9684             if(isNaN(t)){
9685                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9686             }
9687
9688             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9689         },
9690
9691         /**
9692          * Returns the current scroll position of the element.
9693          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9694          */
9695         getScroll : function(){
9696             var d = this.dom, doc = document;
9697             if(d == doc || d == doc.body){
9698                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9699                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9700                 return {left: l, top: t};
9701             }else{
9702                 return {left: d.scrollLeft, top: d.scrollTop};
9703             }
9704         },
9705
9706         /**
9707          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9708          * are convert to standard 6 digit hex color.
9709          * @param {String} attr The css attribute
9710          * @param {String} defaultValue The default value to use when a valid color isn't found
9711          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9712          * YUI color anims.
9713          */
9714         getColor : function(attr, defaultValue, prefix){
9715             var v = this.getStyle(attr);
9716             if(!v || v == "transparent" || v == "inherit") {
9717                 return defaultValue;
9718             }
9719             var color = typeof prefix == "undefined" ? "#" : prefix;
9720             if(v.substr(0, 4) == "rgb("){
9721                 var rvs = v.slice(4, v.length -1).split(",");
9722                 for(var i = 0; i < 3; i++){
9723                     var h = parseInt(rvs[i]).toString(16);
9724                     if(h < 16){
9725                         h = "0" + h;
9726                     }
9727                     color += h;
9728                 }
9729             } else {
9730                 if(v.substr(0, 1) == "#"){
9731                     if(v.length == 4) {
9732                         for(var i = 1; i < 4; i++){
9733                             var c = v.charAt(i);
9734                             color +=  c + c;
9735                         }
9736                     }else if(v.length == 7){
9737                         color += v.substr(1);
9738                     }
9739                 }
9740             }
9741             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9742         },
9743
9744         /**
9745          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9746          * gradient background, rounded corners and a 4-way shadow.
9747          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9748          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9749          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9750          * @return {Roo.Element} this
9751          */
9752         boxWrap : function(cls){
9753             cls = cls || 'x-box';
9754             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9755             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9756             return el;
9757         },
9758
9759         /**
9760          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9761          * @param {String} namespace The namespace in which to look for the attribute
9762          * @param {String} name The attribute name
9763          * @return {String} The attribute value
9764          */
9765         getAttributeNS : Roo.isIE ? function(ns, name){
9766             var d = this.dom;
9767             var type = typeof d[ns+":"+name];
9768             if(type != 'undefined' && type != 'unknown'){
9769                 return d[ns+":"+name];
9770             }
9771             return d[name];
9772         } : function(ns, name){
9773             var d = this.dom;
9774             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9775         },
9776         
9777         
9778         /**
9779          * Sets or Returns the value the dom attribute value
9780          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9781          * @param {String} value (optional) The value to set the attribute to
9782          * @return {String} The attribute value
9783          */
9784         attr : function(name){
9785             if (arguments.length > 1) {
9786                 this.dom.setAttribute(name, arguments[1]);
9787                 return arguments[1];
9788             }
9789             if (typeof(name) == 'object') {
9790                 for(var i in name) {
9791                     this.attr(i, name[i]);
9792                 }
9793                 return name;
9794             }
9795             
9796             
9797             if (!this.dom.hasAttribute(name)) {
9798                 return undefined;
9799             }
9800             return this.dom.getAttribute(name);
9801         }
9802         
9803         
9804         
9805     };
9806
9807     var ep = El.prototype;
9808
9809     /**
9810      * Appends an event handler (Shorthand for addListener)
9811      * @param {String}   eventName     The type of event to append
9812      * @param {Function} fn        The method the event invokes
9813      * @param {Object} scope       (optional) The scope (this object) of the fn
9814      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9815      * @method
9816      */
9817     ep.on = ep.addListener;
9818         // backwards compat
9819     ep.mon = ep.addListener;
9820
9821     /**
9822      * Removes an event handler from this element (shorthand for removeListener)
9823      * @param {String} eventName the type of event to remove
9824      * @param {Function} fn the method the event invokes
9825      * @return {Roo.Element} this
9826      * @method
9827      */
9828     ep.un = ep.removeListener;
9829
9830     /**
9831      * true to automatically adjust width and height settings for box-model issues (default to true)
9832      */
9833     ep.autoBoxAdjust = true;
9834
9835     // private
9836     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9837
9838     // private
9839     El.addUnits = function(v, defaultUnit){
9840         if(v === "" || v == "auto"){
9841             return v;
9842         }
9843         if(v === undefined){
9844             return '';
9845         }
9846         if(typeof v == "number" || !El.unitPattern.test(v)){
9847             return v + (defaultUnit || 'px');
9848         }
9849         return v;
9850     };
9851
9852     // special markup used throughout Roo when box wrapping elements
9853     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9854     /**
9855      * Visibility mode constant - Use visibility to hide element
9856      * @static
9857      * @type Number
9858      */
9859     El.VISIBILITY = 1;
9860     /**
9861      * Visibility mode constant - Use display to hide element
9862      * @static
9863      * @type Number
9864      */
9865     El.DISPLAY = 2;
9866
9867     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9868     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9869     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9870
9871
9872
9873     /**
9874      * @private
9875      */
9876     El.cache = {};
9877
9878     var docEl;
9879
9880     /**
9881      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9882      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9883      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9884      * @return {Element} The Element object
9885      * @static
9886      */
9887     El.get = function(el){
9888         var ex, elm, id;
9889         if(!el){ return null; }
9890         if(typeof el == "string"){ // element id
9891             if(!(elm = document.getElementById(el))){
9892                 return null;
9893             }
9894             if(ex = El.cache[el]){
9895                 ex.dom = elm;
9896             }else{
9897                 ex = El.cache[el] = new El(elm);
9898             }
9899             return ex;
9900         }else if(el.tagName){ // dom element
9901             if(!(id = el.id)){
9902                 id = Roo.id(el);
9903             }
9904             if(ex = El.cache[id]){
9905                 ex.dom = el;
9906             }else{
9907                 ex = El.cache[id] = new El(el);
9908             }
9909             return ex;
9910         }else if(el instanceof El){
9911             if(el != docEl){
9912                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9913                                                               // catch case where it hasn't been appended
9914                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9915             }
9916             return el;
9917         }else if(el.isComposite){
9918             return el;
9919         }else if(el instanceof Array){
9920             return El.select(el);
9921         }else if(el == document){
9922             // create a bogus element object representing the document object
9923             if(!docEl){
9924                 var f = function(){};
9925                 f.prototype = El.prototype;
9926                 docEl = new f();
9927                 docEl.dom = document;
9928             }
9929             return docEl;
9930         }
9931         return null;
9932     };
9933
9934     // private
9935     El.uncache = function(el){
9936         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9937             if(a[i]){
9938                 delete El.cache[a[i].id || a[i]];
9939             }
9940         }
9941     };
9942
9943     // private
9944     // Garbage collection - uncache elements/purge listeners on orphaned elements
9945     // so we don't hold a reference and cause the browser to retain them
9946     El.garbageCollect = function(){
9947         if(!Roo.enableGarbageCollector){
9948             clearInterval(El.collectorThread);
9949             return;
9950         }
9951         for(var eid in El.cache){
9952             var el = El.cache[eid], d = el.dom;
9953             // -------------------------------------------------------
9954             // Determining what is garbage:
9955             // -------------------------------------------------------
9956             // !d
9957             // dom node is null, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.parentNode
9960             // no parentNode == direct orphan, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.offsetParent && !document.getElementById(eid)
9963             // display none elements have no offsetParent so we will
9964             // also try to look it up by it's id. However, check
9965             // offsetParent first so we don't do unneeded lookups.
9966             // This enables collection of elements that are not orphans
9967             // directly, but somewhere up the line they have an orphan
9968             // parent.
9969             // -------------------------------------------------------
9970             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9971                 delete El.cache[eid];
9972                 if(d && Roo.enableListenerCollection){
9973                     E.purgeElement(d);
9974                 }
9975             }
9976         }
9977     }
9978     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9979
9980
9981     // dom is optional
9982     El.Flyweight = function(dom){
9983         this.dom = dom;
9984     };
9985     El.Flyweight.prototype = El.prototype;
9986
9987     El._flyweights = {};
9988     /**
9989      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9990      * the dom node can be overwritten by other code.
9991      * @param {String/HTMLElement} el The dom node or id
9992      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9993      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9994      * @static
9995      * @return {Element} The shared Element object
9996      */
9997     El.fly = function(el, named){
9998         named = named || '_global';
9999         el = Roo.getDom(el);
10000         if(!el){
10001             return null;
10002         }
10003         if(!El._flyweights[named]){
10004             El._flyweights[named] = new El.Flyweight();
10005         }
10006         El._flyweights[named].dom = el;
10007         return El._flyweights[named];
10008     };
10009
10010     /**
10011      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10012      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10013      * Shorthand of {@link Roo.Element#get}
10014      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10015      * @return {Element} The Element object
10016      * @member Roo
10017      * @method get
10018      */
10019     Roo.get = El.get;
10020     /**
10021      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10022      * the dom node can be overwritten by other code.
10023      * Shorthand of {@link Roo.Element#fly}
10024      * @param {String/HTMLElement} el The dom node or id
10025      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10026      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10027      * @static
10028      * @return {Element} The shared Element object
10029      * @member Roo
10030      * @method fly
10031      */
10032     Roo.fly = El.fly;
10033
10034     // speedy lookup for elements never to box adjust
10035     var noBoxAdjust = Roo.isStrict ? {
10036         select:1
10037     } : {
10038         input:1, select:1, textarea:1
10039     };
10040     if(Roo.isIE || Roo.isGecko){
10041         noBoxAdjust['button'] = 1;
10042     }
10043
10044
10045     Roo.EventManager.on(window, 'unload', function(){
10046         delete El.cache;
10047         delete El._flyweights;
10048     });
10049 })();
10050
10051
10052
10053
10054 if(Roo.DomQuery){
10055     Roo.Element.selectorFunction = Roo.DomQuery.select;
10056 }
10057
10058 Roo.Element.select = function(selector, unique, root){
10059     var els;
10060     if(typeof selector == "string"){
10061         els = Roo.Element.selectorFunction(selector, root);
10062     }else if(selector.length !== undefined){
10063         els = selector;
10064     }else{
10065         throw "Invalid selector";
10066     }
10067     if(unique === true){
10068         return new Roo.CompositeElement(els);
10069     }else{
10070         return new Roo.CompositeElementLite(els);
10071     }
10072 };
10073 /**
10074  * Selects elements based on the passed CSS selector to enable working on them as 1.
10075  * @param {String/Array} selector The CSS selector or an array of elements
10076  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10077  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10078  * @return {CompositeElementLite/CompositeElement}
10079  * @member Roo
10080  * @method select
10081  */
10082 Roo.select = Roo.Element.select;
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097 /*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108
10109
10110 //Notifies Element that fx methods are available
10111 Roo.enableFx = true;
10112
10113 /**
10114  * @class Roo.Fx
10115  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10116  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10117  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10118  * Element effects to work.</p><br/>
10119  *
10120  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10121  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10122  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10123  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10124  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10125  * expected results and should be done with care.</p><br/>
10126  *
10127  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10128  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10129 <pre>
10130 Value  Description
10131 -----  -----------------------------
10132 tl     The top left corner
10133 t      The center of the top edge
10134 tr     The top right corner
10135 l      The center of the left edge
10136 r      The center of the right edge
10137 bl     The bottom left corner
10138 b      The center of the bottom edge
10139 br     The bottom right corner
10140 </pre>
10141  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10142  * below are common options that can be passed to any Fx method.</b>
10143  * @cfg {Function} callback A function called when the effect is finished
10144  * @cfg {Object} scope The scope of the effect function
10145  * @cfg {String} easing A valid Easing value for the effect
10146  * @cfg {String} afterCls A css class to apply after the effect
10147  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10148  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10149  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10150  * effects that end with the element being visually hidden, ignored otherwise)
10151  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10152  * a function which returns such a specification that will be applied to the Element after the effect finishes
10153  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10154  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10155  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10156  */
10157 Roo.Fx = {
10158         /**
10159          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10160          * origin for the slide effect.  This function automatically handles wrapping the element with
10161          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10162          * Usage:
10163          *<pre><code>
10164 // default: slide the element in from the top
10165 el.slideIn();
10166
10167 // custom: slide the element in from the right with a 2-second duration
10168 el.slideIn('r', { duration: 2 });
10169
10170 // common config options shown with default values
10171 el.slideIn('t', {
10172     easing: 'easeOut',
10173     duration: .5
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideIn : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // fix display to visibility
10189             this.fixDisplay();
10190
10191             // restore values after effect
10192             var r = this.getFxRestore();
10193             var b = this.getBox();
10194             // fixed size for slide
10195             this.setSize(b);
10196
10197             // wrap if needed
10198             var wrap = this.fxWrap(r.pos, o, "hidden");
10199
10200             var st = this.dom.style;
10201             st.visibility = "visible";
10202             st.position = "absolute";
10203
10204             // clear out temp styles after slide and unwrap
10205             var after = function(){
10206                 el.fxUnwrap(wrap, r.pos, o);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 el.afterFx(o);
10210             };
10211             // time to calc the positions
10212             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10213
10214             switch(anchor.toLowerCase()){
10215                 case "t":
10216                     wrap.setSize(b.width, 0);
10217                     st.left = st.bottom = "0";
10218                     a = {height: bh};
10219                 break;
10220                 case "l":
10221                     wrap.setSize(0, b.height);
10222                     st.right = st.top = "0";
10223                     a = {width: bw};
10224                 break;
10225                 case "r":
10226                     wrap.setSize(0, b.height);
10227                     wrap.setX(b.right);
10228                     st.left = st.top = "0";
10229                     a = {width: bw, points: pt};
10230                 break;
10231                 case "b":
10232                     wrap.setSize(b.width, 0);
10233                     wrap.setY(b.bottom);
10234                     st.left = st.top = "0";
10235                     a = {height: bh, points: pt};
10236                 break;
10237                 case "tl":
10238                     wrap.setSize(0, 0);
10239                     st.right = st.bottom = "0";
10240                     a = {width: bw, height: bh};
10241                 break;
10242                 case "bl":
10243                     wrap.setSize(0, 0);
10244                     wrap.setY(b.y+b.height);
10245                     st.right = st.top = "0";
10246                     a = {width: bw, height: bh, points: pt};
10247                 break;
10248                 case "br":
10249                     wrap.setSize(0, 0);
10250                     wrap.setXY([b.right, b.bottom]);
10251                     st.left = st.top = "0";
10252                     a = {width: bw, height: bh, points: pt};
10253                 break;
10254                 case "tr":
10255                     wrap.setSize(0, 0);
10256                     wrap.setX(b.x+b.width);
10257                     st.left = st.bottom = "0";
10258                     a = {width: bw, height: bh, points: pt};
10259                 break;
10260             }
10261             this.dom.style.visibility = "visible";
10262             wrap.show();
10263
10264             arguments.callee.anim = wrap.fxanim(a,
10265                 o,
10266                 'motion',
10267                 .5,
10268                 'easeOut', after);
10269         });
10270         return this;
10271     },
10272     
10273         /**
10274          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10275          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10276          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10277          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10278          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10279          * Usage:
10280          *<pre><code>
10281 // default: slide the element out to the top
10282 el.slideOut();
10283
10284 // custom: slide the element out to the right with a 2-second duration
10285 el.slideOut('r', { duration: 2 });
10286
10287 // common config options shown with default values
10288 el.slideOut('t', {
10289     easing: 'easeOut',
10290     duration: .5,
10291     remove: false,
10292     useDisplay: false
10293 });
10294 </code></pre>
10295          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10296          * @param {Object} options (optional) Object literal with any of the Fx config options
10297          * @return {Roo.Element} The Element
10298          */
10299     slideOut : function(anchor, o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302
10303         el.queueFx(o, function(){
10304
10305             anchor = anchor || "t";
10306
10307             // restore values after effect
10308             var r = this.getFxRestore();
10309             
10310             var b = this.getBox();
10311             // fixed size for slide
10312             this.setSize(b);
10313
10314             // wrap if needed
10315             var wrap = this.fxWrap(r.pos, o, "visible");
10316
10317             var st = this.dom.style;
10318             st.visibility = "visible";
10319             st.position = "absolute";
10320
10321             wrap.setSize(b);
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.fxUnwrap(wrap, r.pos, o);
10331
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             var a, zero = {to: 0};
10339             switch(anchor.toLowerCase()){
10340                 case "t":
10341                     st.left = st.bottom = "0";
10342                     a = {height: zero};
10343                 break;
10344                 case "l":
10345                     st.right = st.top = "0";
10346                     a = {width: zero};
10347                 break;
10348                 case "r":
10349                     st.left = st.top = "0";
10350                     a = {width: zero, points: {to:[b.right, b.y]}};
10351                 break;
10352                 case "b":
10353                     st.left = st.top = "0";
10354                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10355                 break;
10356                 case "tl":
10357                     st.right = st.bottom = "0";
10358                     a = {width: zero, height: zero};
10359                 break;
10360                 case "bl":
10361                     st.right = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10363                 break;
10364                 case "br":
10365                     st.left = st.top = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10367                 break;
10368                 case "tr":
10369                     st.left = st.bottom = "0";
10370                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10371                 break;
10372             }
10373
10374             arguments.callee.anim = wrap.fxanim(a,
10375                 o,
10376                 'motion',
10377                 .5,
10378                 "easeOut", after);
10379         });
10380         return this;
10381     },
10382
10383         /**
10384          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10385          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10386          * The element must be removed from the DOM using the 'remove' config option if desired.
10387          * Usage:
10388          *<pre><code>
10389 // default
10390 el.puff();
10391
10392 // common config options shown with default values
10393 el.puff({
10394     easing: 'easeOut',
10395     duration: .5,
10396     remove: false,
10397     useDisplay: false
10398 });
10399 </code></pre>
10400          * @param {Object} options (optional) Object literal with any of the Fx config options
10401          * @return {Roo.Element} The Element
10402          */
10403     puff : function(o){
10404         var el = this.getFxEl();
10405         o = o || {};
10406
10407         el.queueFx(o, function(){
10408             this.clearOpacity();
10409             this.show();
10410
10411             // restore values after effect
10412             var r = this.getFxRestore();
10413             var st = this.dom.style;
10414
10415             var after = function(){
10416                 if(o.useDisplay){
10417                     el.setDisplayed(false);
10418                 }else{
10419                     el.hide();
10420                 }
10421
10422                 el.clearOpacity();
10423
10424                 el.setPositioning(r.pos);
10425                 st.width = r.width;
10426                 st.height = r.height;
10427                 st.fontSize = '';
10428                 el.afterFx(o);
10429             };
10430
10431             var width = this.getWidth();
10432             var height = this.getHeight();
10433
10434             arguments.callee.anim = this.fxanim({
10435                     width : {to: this.adjustWidth(width * 2)},
10436                     height : {to: this.adjustHeight(height * 2)},
10437                     points : {by: [-(width * .5), -(height * .5)]},
10438                     opacity : {to: 0},
10439                     fontSize: {to:200, unit: "%"}
10440                 },
10441                 o,
10442                 'motion',
10443                 .5,
10444                 "easeOut", after);
10445         });
10446         return this;
10447     },
10448
10449         /**
10450          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10451          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10452          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10453          * Usage:
10454          *<pre><code>
10455 // default
10456 el.switchOff();
10457
10458 // all config options shown with default values
10459 el.switchOff({
10460     easing: 'easeIn',
10461     duration: .3,
10462     remove: false,
10463     useDisplay: false
10464 });
10465 </code></pre>
10466          * @param {Object} options (optional) Object literal with any of the Fx config options
10467          * @return {Roo.Element} The Element
10468          */
10469     switchOff : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472
10473         el.queueFx(o, function(){
10474             this.clearOpacity();
10475             this.clip();
10476
10477             // restore values after effect
10478             var r = this.getFxRestore();
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10497                 this.clearOpacity();
10498                 (function(){
10499                     this.fxanim({
10500                         height:{to:1},
10501                         points:{by:[0, this.getHeight() * .5]}
10502                     }, o, 'motion', 0.3, 'easeIn', after);
10503                 }).defer(100, this);
10504             });
10505         });
10506         return this;
10507     },
10508
10509     /**
10510      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10511      * changed using the "attr" config option) and then fading back to the original color. If no original
10512      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10513      * Usage:
10514 <pre><code>
10515 // default: highlight background to yellow
10516 el.highlight();
10517
10518 // custom: highlight foreground text to blue for 2 seconds
10519 el.highlight("0000ff", { attr: 'color', duration: 2 });
10520
10521 // common config options shown with default values
10522 el.highlight("ffff9c", {
10523     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10524     endColor: (current color) or "ffffff",
10525     easing: 'easeIn',
10526     duration: 1
10527 });
10528 </code></pre>
10529      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10530      * @param {Object} options (optional) Object literal with any of the Fx config options
10531      * @return {Roo.Element} The Element
10532      */ 
10533     highlight : function(color, o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536
10537         el.queueFx(o, function(){
10538             color = color || "ffff9c";
10539             attr = o.attr || "backgroundColor";
10540
10541             this.clearOpacity();
10542             this.show();
10543
10544             var origColor = this.getColor(attr);
10545             var restoreColor = this.dom.style[attr];
10546             endColor = (o.endColor || origColor) || "ffffff";
10547
10548             var after = function(){
10549                 el.dom.style[attr] = restoreColor;
10550                 el.afterFx(o);
10551             };
10552
10553             var a = {};
10554             a[attr] = {from: color, to: endColor};
10555             arguments.callee.anim = this.fxanim(a,
10556                 o,
10557                 'color',
10558                 1,
10559                 'easeIn', after);
10560         });
10561         return this;
10562     },
10563
10564    /**
10565     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10566     * Usage:
10567 <pre><code>
10568 // default: a single light blue ripple
10569 el.frame();
10570
10571 // custom: 3 red ripples lasting 3 seconds total
10572 el.frame("ff0000", 3, { duration: 3 });
10573
10574 // common config options shown with default values
10575 el.frame("C3DAF9", 1, {
10576     duration: 1 //duration of entire animation (not each individual ripple)
10577     // Note: Easing is not configurable and will be ignored if included
10578 });
10579 </code></pre>
10580     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10581     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10582     * @param {Object} options (optional) Object literal with any of the Fx config options
10583     * @return {Roo.Element} The Element
10584     */
10585     frame : function(color, count, o){
10586         var el = this.getFxEl();
10587         o = o || {};
10588
10589         el.queueFx(o, function(){
10590             color = color || "#C3DAF9";
10591             if(color.length == 6){
10592                 color = "#" + color;
10593             }
10594             count = count || 1;
10595             duration = o.duration || 1;
10596             this.show();
10597
10598             var b = this.getBox();
10599             var animFn = function(){
10600                 var proxy = this.createProxy({
10601
10602                      style:{
10603                         visbility:"hidden",
10604                         position:"absolute",
10605                         "z-index":"35000", // yee haw
10606                         border:"0px solid " + color
10607                      }
10608                   });
10609                 var scale = Roo.isBorderBox ? 2 : 1;
10610                 proxy.animate({
10611                     top:{from:b.y, to:b.y - 20},
10612                     left:{from:b.x, to:b.x - 20},
10613                     borderWidth:{from:0, to:10},
10614                     opacity:{from:1, to:0},
10615                     height:{from:b.height, to:(b.height + (20*scale))},
10616                     width:{from:b.width, to:(b.width + (20*scale))}
10617                 }, duration, function(){
10618                     proxy.remove();
10619                 });
10620                 if(--count > 0){
10621                      animFn.defer((duration/2)*1000, this);
10622                 }else{
10623                     el.afterFx(o);
10624                 }
10625             };
10626             animFn.call(this);
10627         });
10628         return this;
10629     },
10630
10631    /**
10632     * Creates a pause before any subsequent queued effects begin.  If there are
10633     * no effects queued after the pause it will have no effect.
10634     * Usage:
10635 <pre><code>
10636 el.pause(1);
10637 </code></pre>
10638     * @param {Number} seconds The length of time to pause (in seconds)
10639     * @return {Roo.Element} The Element
10640     */
10641     pause : function(seconds){
10642         var el = this.getFxEl();
10643         var o = {};
10644
10645         el.queueFx(o, function(){
10646             setTimeout(function(){
10647                 el.afterFx(o);
10648             }, seconds * 1000);
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10655     * using the "endOpacity" config option.
10656     * Usage:
10657 <pre><code>
10658 // default: fade in from opacity 0 to 100%
10659 el.fadeIn();
10660
10661 // custom: fade in from opacity 0 to 75% over 2 seconds
10662 el.fadeIn({ endOpacity: .75, duration: 2});
10663
10664 // common config options shown with default values
10665 el.fadeIn({
10666     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10667     easing: 'easeOut',
10668     duration: .5
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeIn : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             this.setOpacity(0);
10679             this.fixDisplay();
10680             this.dom.style.visibility = 'visible';
10681             var to = o.endOpacity || 1;
10682             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10683                 o, null, .5, "easeOut", function(){
10684                 if(to == 1){
10685                     this.clearOpacity();
10686                 }
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10695     * using the "endOpacity" config option.
10696     * Usage:
10697 <pre><code>
10698 // default: fade out from the element's current opacity to 0
10699 el.fadeOut();
10700
10701 // custom: fade out from the element's current opacity to 25% over 2 seconds
10702 el.fadeOut({ endOpacity: .25, duration: 2});
10703
10704 // common config options shown with default values
10705 el.fadeOut({
10706     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10707     easing: 'easeOut',
10708     duration: .5
10709     remove: false,
10710     useDisplay: false
10711 });
10712 </code></pre>
10713     * @param {Object} options (optional) Object literal with any of the Fx config options
10714     * @return {Roo.Element} The Element
10715     */
10716     fadeOut : function(o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719         el.queueFx(o, function(){
10720             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10721                 o, null, .5, "easeOut", function(){
10722                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10723                      this.dom.style.display = "none";
10724                 }else{
10725                      this.dom.style.visibility = "hidden";
10726                 }
10727                 this.clearOpacity();
10728                 el.afterFx(o);
10729             });
10730         });
10731         return this;
10732     },
10733
10734    /**
10735     * Animates the transition of an element's dimensions from a starting height/width
10736     * to an ending height/width.
10737     * Usage:
10738 <pre><code>
10739 // change height and width to 100x100 pixels
10740 el.scale(100, 100);
10741
10742 // common config options shown with default values.  The height and width will default to
10743 // the element's existing values if passed as null.
10744 el.scale(
10745     [element's width],
10746     [element's height], {
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Number} width  The new width (pass undefined to keep the original width)
10752     * @param {Number} height  The new height (pass undefined to keep the original height)
10753     * @param {Object} options (optional) Object literal with any of the Fx config options
10754     * @return {Roo.Element} The Element
10755     */
10756     scale : function(w, h, o){
10757         this.shift(Roo.apply({}, o, {
10758             width: w,
10759             height: h
10760         }));
10761         return this;
10762     },
10763
10764    /**
10765     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10766     * Any of these properties not specified in the config object will not be changed.  This effect 
10767     * requires that at least one new dimension, position or opacity setting must be passed in on
10768     * the config object in order for the function to have any effect.
10769     * Usage:
10770 <pre><code>
10771 // slide the element horizontally to x position 200 while changing the height and opacity
10772 el.shift({ x: 200, height: 50, opacity: .8 });
10773
10774 // common config options shown with default values.
10775 el.shift({
10776     width: [element's width],
10777     height: [element's height],
10778     x: [element's x position],
10779     y: [element's y position],
10780     opacity: [element's opacity],
10781     easing: 'easeOut',
10782     duration: .35
10783 });
10784 </code></pre>
10785     * @param {Object} options  Object literal with any of the Fx config options
10786     * @return {Roo.Element} The Element
10787     */
10788     shift : function(o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791         el.queueFx(o, function(){
10792             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10793             if(w !== undefined){
10794                 a.width = {to: this.adjustWidth(w)};
10795             }
10796             if(h !== undefined){
10797                 a.height = {to: this.adjustHeight(h)};
10798             }
10799             if(x !== undefined || y !== undefined){
10800                 a.points = {to: [
10801                     x !== undefined ? x : this.getX(),
10802                     y !== undefined ? y : this.getY()
10803                 ]};
10804             }
10805             if(op !== undefined){
10806                 a.opacity = {to: op};
10807             }
10808             if(o.xy !== undefined){
10809                 a.points = {to: o.xy};
10810             }
10811             arguments.callee.anim = this.fxanim(a,
10812                 o, 'motion', .35, "easeOut", function(){
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819         /**
10820          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10821          * ending point of the effect.
10822          * Usage:
10823          *<pre><code>
10824 // default: slide the element downward while fading out
10825 el.ghost();
10826
10827 // custom: slide the element out to the right with a 2-second duration
10828 el.ghost('r', { duration: 2 });
10829
10830 // common config options shown with default values
10831 el.ghost('b', {
10832     easing: 'easeOut',
10833     duration: .5
10834     remove: false,
10835     useDisplay: false
10836 });
10837 </code></pre>
10838          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10839          * @param {Object} options (optional) Object literal with any of the Fx config options
10840          * @return {Roo.Element} The Element
10841          */
10842     ghost : function(anchor, o){
10843         var el = this.getFxEl();
10844         o = o || {};
10845
10846         el.queueFx(o, function(){
10847             anchor = anchor || "b";
10848
10849             // restore values after effect
10850             var r = this.getFxRestore();
10851             var w = this.getWidth(),
10852                 h = this.getHeight();
10853
10854             var st = this.dom.style;
10855
10856             var after = function(){
10857                 if(o.useDisplay){
10858                     el.setDisplayed(false);
10859                 }else{
10860                     el.hide();
10861                 }
10862
10863                 el.clearOpacity();
10864                 el.setPositioning(r.pos);
10865                 st.width = r.width;
10866                 st.height = r.height;
10867
10868                 el.afterFx(o);
10869             };
10870
10871             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10872             switch(anchor.toLowerCase()){
10873                 case "t":
10874                     pt.by = [0, -h];
10875                 break;
10876                 case "l":
10877                     pt.by = [-w, 0];
10878                 break;
10879                 case "r":
10880                     pt.by = [w, 0];
10881                 break;
10882                 case "b":
10883                     pt.by = [0, h];
10884                 break;
10885                 case "tl":
10886                     pt.by = [-w, -h];
10887                 break;
10888                 case "bl":
10889                     pt.by = [-w, h];
10890                 break;
10891                 case "br":
10892                     pt.by = [w, h];
10893                 break;
10894                 case "tr":
10895                     pt.by = [w, -h];
10896                 break;
10897             }
10898
10899             arguments.callee.anim = this.fxanim(a,
10900                 o,
10901                 'motion',
10902                 .5,
10903                 "easeOut", after);
10904         });
10905         return this;
10906     },
10907
10908         /**
10909          * Ensures that all effects queued after syncFx is called on the element are
10910          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10911          * @return {Roo.Element} The Element
10912          */
10913     syncFx : function(){
10914         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10915             block : false,
10916             concurrent : true,
10917             stopFx : false
10918         });
10919         return this;
10920     },
10921
10922         /**
10923          * Ensures that all effects queued after sequenceFx is called on the element are
10924          * run in sequence.  This is the opposite of {@link #syncFx}.
10925          * @return {Roo.Element} The Element
10926          */
10927     sequenceFx : function(){
10928         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10929             block : false,
10930             concurrent : false,
10931             stopFx : false
10932         });
10933         return this;
10934     },
10935
10936         /* @private */
10937     nextFx : function(){
10938         var ef = this.fxQueue[0];
10939         if(ef){
10940             ef.call(this);
10941         }
10942     },
10943
10944         /**
10945          * Returns true if the element has any effects actively running or queued, else returns false.
10946          * @return {Boolean} True if element has active effects, else false
10947          */
10948     hasActiveFx : function(){
10949         return this.fxQueue && this.fxQueue[0];
10950     },
10951
10952         /**
10953          * Stops any running effects and clears the element's internal effects queue if it contains
10954          * any additional effects that haven't started yet.
10955          * @return {Roo.Element} The Element
10956          */
10957     stopFx : function(){
10958         if(this.hasActiveFx()){
10959             var cur = this.fxQueue[0];
10960             if(cur && cur.anim && cur.anim.isAnimated()){
10961                 this.fxQueue = [cur]; // clear out others
10962                 cur.anim.stop(true);
10963             }
10964         }
10965         return this;
10966     },
10967
10968         /* @private */
10969     beforeFx : function(o){
10970         if(this.hasActiveFx() && !o.concurrent){
10971            if(o.stopFx){
10972                this.stopFx();
10973                return true;
10974            }
10975            return false;
10976         }
10977         return true;
10978     },
10979
10980         /**
10981          * Returns true if the element is currently blocking so that no other effect can be queued
10982          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10983          * used to ensure that an effect initiated by a user action runs to completion prior to the
10984          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10985          * @return {Boolean} True if blocking, else false
10986          */
10987     hasFxBlock : function(){
10988         var q = this.fxQueue;
10989         return q && q[0] && q[0].block;
10990     },
10991
10992         /* @private */
10993     queueFx : function(o, fn){
10994         if(!this.fxQueue){
10995             this.fxQueue = [];
10996         }
10997         if(!this.hasFxBlock()){
10998             Roo.applyIf(o, this.fxDefaults);
10999             if(!o.concurrent){
11000                 var run = this.beforeFx(o);
11001                 fn.block = o.block;
11002                 this.fxQueue.push(fn);
11003                 if(run){
11004                     this.nextFx();
11005                 }
11006             }else{
11007                 fn.call(this);
11008             }
11009         }
11010         return this;
11011     },
11012
11013         /* @private */
11014     fxWrap : function(pos, o, vis){
11015         var wrap;
11016         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11017             var wrapXY;
11018             if(o.fixPosition){
11019                 wrapXY = this.getXY();
11020             }
11021             var div = document.createElement("div");
11022             div.style.visibility = vis;
11023             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11024             wrap.setPositioning(pos);
11025             if(wrap.getStyle("position") == "static"){
11026                 wrap.position("relative");
11027             }
11028             this.clearPositioning('auto');
11029             wrap.clip();
11030             wrap.dom.appendChild(this.dom);
11031             if(wrapXY){
11032                 wrap.setXY(wrapXY);
11033             }
11034         }
11035         return wrap;
11036     },
11037
11038         /* @private */
11039     fxUnwrap : function(wrap, pos, o){
11040         this.clearPositioning();
11041         this.setPositioning(pos);
11042         if(!o.wrap){
11043             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11044             wrap.remove();
11045         }
11046     },
11047
11048         /* @private */
11049     getFxRestore : function(){
11050         var st = this.dom.style;
11051         return {pos: this.getPositioning(), width: st.width, height : st.height};
11052     },
11053
11054         /* @private */
11055     afterFx : function(o){
11056         if(o.afterStyle){
11057             this.applyStyles(o.afterStyle);
11058         }
11059         if(o.afterCls){
11060             this.addClass(o.afterCls);
11061         }
11062         if(o.remove === true){
11063             this.remove();
11064         }
11065         Roo.callback(o.callback, o.scope, [this]);
11066         if(!o.concurrent){
11067             this.fxQueue.shift();
11068             this.nextFx();
11069         }
11070     },
11071
11072         /* @private */
11073     getFxEl : function(){ // support for composite element fx
11074         return Roo.get(this.dom);
11075     },
11076
11077         /* @private */
11078     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11079         animType = animType || 'run';
11080         opt = opt || {};
11081         var anim = Roo.lib.Anim[animType](
11082             this.dom, args,
11083             (opt.duration || defaultDur) || .35,
11084             (opt.easing || defaultEase) || 'easeOut',
11085             function(){
11086                 Roo.callback(cb, this);
11087             },
11088             this
11089         );
11090         opt.anim = anim;
11091         return anim;
11092     }
11093 };
11094
11095 // backwords compat
11096 Roo.Fx.resize = Roo.Fx.scale;
11097
11098 //When included, Roo.Fx is automatically applied to Element so that all basic
11099 //effects are available directly via the Element API
11100 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11101  * Based on:
11102  * Ext JS Library 1.1.1
11103  * Copyright(c) 2006-2007, Ext JS, LLC.
11104  *
11105  * Originally Released Under LGPL - original licence link has changed is not relivant.
11106  *
11107  * Fork - LGPL
11108  * <script type="text/javascript">
11109  */
11110
11111
11112 /**
11113  * @class Roo.CompositeElement
11114  * Standard composite class. Creates a Roo.Element for every element in the collection.
11115  * <br><br>
11116  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11117  * actions will be performed on all the elements in this collection.</b>
11118  * <br><br>
11119  * All methods return <i>this</i> and can be chained.
11120  <pre><code>
11121  var els = Roo.select("#some-el div.some-class", true);
11122  // or select directly from an existing element
11123  var el = Roo.get('some-el');
11124  el.select('div.some-class', true);
11125
11126  els.setWidth(100); // all elements become 100 width
11127  els.hide(true); // all elements fade out and hide
11128  // or
11129  els.setWidth(100).hide(true);
11130  </code></pre>
11131  */
11132 Roo.CompositeElement = function(els){
11133     this.elements = [];
11134     this.addElements(els);
11135 };
11136 Roo.CompositeElement.prototype = {
11137     isComposite: true,
11138     addElements : function(els){
11139         if(!els) {
11140             return this;
11141         }
11142         if(typeof els == "string"){
11143             els = Roo.Element.selectorFunction(els);
11144         }
11145         var yels = this.elements;
11146         var index = yels.length-1;
11147         for(var i = 0, len = els.length; i < len; i++) {
11148                 yels[++index] = Roo.get(els[i]);
11149         }
11150         return this;
11151     },
11152
11153     /**
11154     * Clears this composite and adds the elements returned by the passed selector.
11155     * @param {String/Array} els A string CSS selector, an array of elements or an element
11156     * @return {CompositeElement} this
11157     */
11158     fill : function(els){
11159         this.elements = [];
11160         this.add(els);
11161         return this;
11162     },
11163
11164     /**
11165     * Filters this composite to only elements that match the passed selector.
11166     * @param {String} selector A string CSS selector
11167     * @param {Boolean} inverse return inverse filter (not matches)
11168     * @return {CompositeElement} this
11169     */
11170     filter : function(selector, inverse){
11171         var els = [];
11172         inverse = inverse || false;
11173         this.each(function(el){
11174             var match = inverse ? !el.is(selector) : el.is(selector);
11175             if(match){
11176                 els[els.length] = el.dom;
11177             }
11178         });
11179         this.fill(els);
11180         return this;
11181     },
11182
11183     invoke : function(fn, args){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186                 Roo.Element.prototype[fn].apply(els[i], args);
11187         }
11188         return this;
11189     },
11190     /**
11191     * Adds elements to this composite.
11192     * @param {String/Array} els A string CSS selector, an array of elements or an element
11193     * @return {CompositeElement} this
11194     */
11195     add : function(els){
11196         if(typeof els == "string"){
11197             this.addElements(Roo.Element.selectorFunction(els));
11198         }else if(els.length !== undefined){
11199             this.addElements(els);
11200         }else{
11201             this.addElements([els]);
11202         }
11203         return this;
11204     },
11205     /**
11206     * Calls the passed function passing (el, this, index) for each element in this composite.
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             if(fn.call(scope || els[i], els[i], this, i) === false) {
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     /**
11222      * Returns the Element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         return this.elements[index] || null;
11228     },
11229
11230     /**
11231      * Returns the first Element
11232      * @return {Roo.Element}
11233      */
11234     first : function(){
11235         return this.item(0);
11236     },
11237
11238     /**
11239      * Returns the last Element
11240      * @return {Roo.Element}
11241      */
11242     last : function(){
11243         return this.item(this.elements.length-1);
11244     },
11245
11246     /**
11247      * Returns the number of elements in this composite
11248      * @return Number
11249      */
11250     getCount : function(){
11251         return this.elements.length;
11252     },
11253
11254     /**
11255      * Returns true if this composite contains the passed element
11256      * @return Boolean
11257      */
11258     contains : function(el){
11259         return this.indexOf(el) !== -1;
11260     },
11261
11262     /**
11263      * Returns true if this composite contains the passed element
11264      * @return Boolean
11265      */
11266     indexOf : function(el){
11267         return this.elements.indexOf(Roo.get(el));
11268     },
11269
11270
11271     /**
11272     * Removes the specified element(s).
11273     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11274     * or an array of any of those.
11275     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11276     * @return {CompositeElement} this
11277     */
11278     removeElement : function(el, removeDom){
11279         if(el instanceof Array){
11280             for(var i = 0, len = el.length; i < len; i++){
11281                 this.removeElement(el[i]);
11282             }
11283             return this;
11284         }
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             if(removeDom){
11288                 var d = this.elements[index];
11289                 if(d.dom){
11290                     d.remove();
11291                 }else{
11292                     d.parentNode.removeChild(d);
11293                 }
11294             }
11295             this.elements.splice(index, 1);
11296         }
11297         return this;
11298     },
11299
11300     /**
11301     * Replaces the specified element with the passed element.
11302     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11303     * to replace.
11304     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11305     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11306     * @return {CompositeElement} this
11307     */
11308     replaceElement : function(el, replacement, domReplace){
11309         var index = typeof el == 'number' ? el : this.indexOf(el);
11310         if(index !== -1){
11311             if(domReplace){
11312                 this.elements[index].replaceWith(replacement);
11313             }else{
11314                 this.elements.splice(index, 1, Roo.get(replacement))
11315             }
11316         }
11317         return this;
11318     },
11319
11320     /**
11321      * Removes all elements.
11322      */
11323     clear : function(){
11324         this.elements = [];
11325     }
11326 };
11327 (function(){
11328     Roo.CompositeElement.createCall = function(proto, fnName){
11329         if(!proto[fnName]){
11330             proto[fnName] = function(){
11331                 return this.invoke(fnName, arguments);
11332             };
11333         }
11334     };
11335     for(var fnName in Roo.Element.prototype){
11336         if(typeof Roo.Element.prototype[fnName] == "function"){
11337             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11338         }
11339     };
11340 })();
11341 /*
11342  * Based on:
11343  * Ext JS Library 1.1.1
11344  * Copyright(c) 2006-2007, Ext JS, LLC.
11345  *
11346  * Originally Released Under LGPL - original licence link has changed is not relivant.
11347  *
11348  * Fork - LGPL
11349  * <script type="text/javascript">
11350  */
11351
11352 /**
11353  * @class Roo.CompositeElementLite
11354  * @extends Roo.CompositeElement
11355  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11356  <pre><code>
11357  var els = Roo.select("#some-el div.some-class");
11358  // or select directly from an existing element
11359  var el = Roo.get('some-el');
11360  el.select('div.some-class');
11361
11362  els.setWidth(100); // all elements become 100 width
11363  els.hide(true); // all elements fade out and hide
11364  // or
11365  els.setWidth(100).hide(true);
11366  </code></pre><br><br>
11367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11368  * actions will be performed on all the elements in this collection.</b>
11369  */
11370 Roo.CompositeElementLite = function(els){
11371     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11372     this.el = new Roo.Element.Flyweight();
11373 };
11374 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11375     addElements : function(els){
11376         if(els){
11377             if(els instanceof Array){
11378                 this.elements = this.elements.concat(els);
11379             }else{
11380                 var yels = this.elements;
11381                 var index = yels.length-1;
11382                 for(var i = 0, len = els.length; i < len; i++) {
11383                     yels[++index] = els[i];
11384                 }
11385             }
11386         }
11387         return this;
11388     },
11389     invoke : function(fn, args){
11390         var els = this.elements;
11391         var el = this.el;
11392         for(var i = 0, len = els.length; i < len; i++) {
11393             el.dom = els[i];
11394                 Roo.Element.prototype[fn].apply(el, args);
11395         }
11396         return this;
11397     },
11398     /**
11399      * Returns a flyweight Element of the dom element object at the specified index
11400      * @param {Number} index
11401      * @return {Roo.Element}
11402      */
11403     item : function(index){
11404         if(!this.elements[index]){
11405             return null;
11406         }
11407         this.el.dom = this.elements[index];
11408         return this.el;
11409     },
11410
11411     // fixes scope with flyweight
11412     addListener : function(eventName, handler, scope, opt){
11413         var els = this.elements;
11414         for(var i = 0, len = els.length; i < len; i++) {
11415             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11416         }
11417         return this;
11418     },
11419
11420     /**
11421     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11422     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11423     * a reference to the dom node, use el.dom.</b>
11424     * @param {Function} fn The function to call
11425     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11426     * @return {CompositeElement} this
11427     */
11428     each : function(fn, scope){
11429         var els = this.elements;
11430         var el = this.el;
11431         for(var i = 0, len = els.length; i < len; i++){
11432             el.dom = els[i];
11433                 if(fn.call(scope || el, el, this, i) === false){
11434                 break;
11435             }
11436         }
11437         return this;
11438     },
11439
11440     indexOf : function(el){
11441         return this.elements.indexOf(Roo.getDom(el));
11442     },
11443
11444     replaceElement : function(el, replacement, domReplace){
11445         var index = typeof el == 'number' ? el : this.indexOf(el);
11446         if(index !== -1){
11447             replacement = Roo.getDom(replacement);
11448             if(domReplace){
11449                 var d = this.elements[index];
11450                 d.parentNode.insertBefore(replacement, d);
11451                 d.parentNode.removeChild(d);
11452             }
11453             this.elements.splice(index, 1, replacement);
11454         }
11455         return this;
11456     }
11457 });
11458 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11459
11460 /*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471  
11472
11473 /**
11474  * @class Roo.data.Connection
11475  * @extends Roo.util.Observable
11476  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11477  * either to a configured URL, or to a URL specified at request time. 
11478  * 
11479  * Requests made by this class are asynchronous, and will return immediately. No data from
11480  * the server will be available to the statement immediately following the {@link #request} call.
11481  * To process returned data, use a callback in the request options object, or an event listener.
11482  * 
11483  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11484  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11485  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11486  * property and, if present, the IFRAME's XML document as the responseXML property.
11487  * 
11488  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11489  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11490  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11491  * standard DOM methods.
11492  * @constructor
11493  * @param {Object} config a configuration object.
11494  */
11495 Roo.data.Connection = function(config){
11496     Roo.apply(this, config);
11497     this.addEvents({
11498         /**
11499          * @event beforerequest
11500          * Fires before a network request is made to retrieve a data object.
11501          * @param {Connection} conn This Connection object.
11502          * @param {Object} options The options config object passed to the {@link #request} method.
11503          */
11504         "beforerequest" : true,
11505         /**
11506          * @event requestcomplete
11507          * Fires if the request was successfully completed.
11508          * @param {Connection} conn This Connection object.
11509          * @param {Object} response The XHR object containing the response data.
11510          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11511          * @param {Object} options The options config object passed to the {@link #request} method.
11512          */
11513         "requestcomplete" : true,
11514         /**
11515          * @event requestexception
11516          * Fires if an error HTTP status was returned from the server.
11517          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11518          * @param {Connection} conn This Connection object.
11519          * @param {Object} response The XHR object containing the response data.
11520          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11521          * @param {Object} options The options config object passed to the {@link #request} method.
11522          */
11523         "requestexception" : true
11524     });
11525     Roo.data.Connection.superclass.constructor.call(this);
11526 };
11527
11528 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11529     /**
11530      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11538      *  to each request made by this object. (defaults to undefined)
11539      */
11540     /**
11541      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11542      */
11543     /**
11544      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11545      */
11546     timeout : 30000,
11547     /**
11548      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort:false,
11552
11553     /**
11554      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11555      * @type Boolean
11556      */
11557     disableCaching: true,
11558
11559     /**
11560      * Sends an HTTP request to a remote server.
11561      * @param {Object} options An object which may contain the following properties:<ul>
11562      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11563      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11564      * request, a url encoded string or a function to call to get either.</li>
11565      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11566      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11567      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11568      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11569      * <li>options {Object} The parameter to the request call.</li>
11570      * <li>success {Boolean} True if the request succeeded.</li>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * </ul></li>
11573      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11574      * The callback is passed the following parameters:<ul>
11575      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11576      * <li>options {Object} The parameter to the request call.</li>
11577      * </ul></li>
11578      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11579      * The callback is passed the following parameters:<ul>
11580      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11581      * <li>options {Object} The parameter to the request call.</li>
11582      * </ul></li>
11583      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11584      * for the callback function. Defaults to the browser window.</li>
11585      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11586      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11587      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11588      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11589      * params for the post data. Any params will be appended to the URL.</li>
11590      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11591      * </ul>
11592      * @return {Number} transactionId
11593      */
11594     request : function(o){
11595         if(this.fireEvent("beforerequest", this, o) !== false){
11596             var p = o.params;
11597
11598             if(typeof p == "function"){
11599                 p = p.call(o.scope||window, o);
11600             }
11601             if(typeof p == "object"){
11602                 p = Roo.urlEncode(o.params);
11603             }
11604             if(this.extraParams){
11605                 var extras = Roo.urlEncode(this.extraParams);
11606                 p = p ? (p + '&' + extras) : extras;
11607             }
11608
11609             var url = o.url || this.url;
11610             if(typeof url == 'function'){
11611                 url = url.call(o.scope||window, o);
11612             }
11613
11614             if(o.form){
11615                 var form = Roo.getDom(o.form);
11616                 url = url || form.action;
11617
11618                 var enctype = form.getAttribute("enctype");
11619                 
11620                 if (o.formData) {
11621                     return this.doFormDataUpload(o,p,url);
11622                 }
11623                 
11624                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11625                     return this.doFormUpload(o, p, url);
11626                 }
11627                 var f = Roo.lib.Ajax.serializeForm(form);
11628                 p = p ? (p + '&' + f) : f;
11629             }
11630             
11631             if (!o.form && o.formData) { 
11632                 return this.doFormDataUpload(o,p,url);
11633             }
11634             
11635
11636             var hs = o.headers;
11637             if(this.defaultHeaders){
11638                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11639                 if(!o.headers){
11640                     o.headers = hs;
11641                 }
11642             }
11643
11644             var cb = {
11645                 success: this.handleResponse,
11646                 failure: this.handleFailure,
11647                 scope: this,
11648                 argument: {options: o},
11649                 timeout : o.timeout || this.timeout
11650             };
11651
11652             var method = o.method||this.method||(p ? "POST" : "GET");
11653
11654             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11655                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11656             }
11657
11658             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11659                 if(o.autoAbort){
11660                     this.abort();
11661                 }
11662             }else if(this.autoAbort !== false){
11663                 this.abort();
11664             }
11665
11666             if((method == 'GET' && p) || o.xmlData){
11667                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11668                 p = '';
11669             }
11670             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11671             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11672             Roo.lib.Ajax.useDefaultHeader == true;
11673             return this.transId;
11674         }else{
11675             Roo.callback(o.callback, o.scope, [o, null, null]);
11676             return null;
11677         }
11678     },
11679
11680     /**
11681      * Determine whether this object has a request outstanding.
11682      * @param {Number} transactionId (Optional) defaults to the last transaction
11683      * @return {Boolean} True if there is an outstanding request.
11684      */
11685     isLoading : function(transId){
11686         if(transId){
11687             return Roo.lib.Ajax.isCallInProgress(transId);
11688         }else{
11689             return this.transId ? true : false;
11690         }
11691     },
11692
11693     /**
11694      * Aborts any outstanding request.
11695      * @param {Number} transactionId (Optional) defaults to the last transaction
11696      */
11697     abort : function(transId){
11698         if(transId || this.isLoading()){
11699             Roo.lib.Ajax.abort(transId || this.transId);
11700         }
11701     },
11702
11703     // private
11704     handleResponse : function(response){
11705         this.transId = false;
11706         var options = response.argument.options;
11707         response.argument = options ? options.argument : null;
11708         this.fireEvent("requestcomplete", this, response, options);
11709         Roo.callback(options.success, options.scope, [response, options]);
11710         Roo.callback(options.callback, options.scope, [options, true, response]);
11711     },
11712
11713     // private
11714     handleFailure : function(response, e){
11715         this.transId = false;
11716         var options = response.argument.options;
11717         response.argument = options ? options.argument : null;
11718         this.fireEvent("requestexception", this, response, options, e);
11719         Roo.callback(options.failure, options.scope, [response, options]);
11720         Roo.callback(options.callback, options.scope, [options, false, response]);
11721     },
11722
11723     // private
11724     doFormUpload : function(o, ps, url){
11725         var id = Roo.id();
11726         var frame = document.createElement('iframe');
11727         frame.id = id;
11728         frame.name = id;
11729         frame.className = 'x-hidden';
11730         if(Roo.isIE){
11731             frame.src = Roo.SSL_SECURE_URL;
11732         }
11733         document.body.appendChild(frame);
11734
11735         if(Roo.isIE){
11736            document.frames[id].name = id;
11737         }
11738
11739         var form = Roo.getDom(o.form);
11740         form.target = id;
11741         form.method = 'POST';
11742         form.enctype = form.encoding = 'multipart/form-data';
11743         if(url){
11744             form.action = url;
11745         }
11746
11747         var hiddens, hd;
11748         if(ps){ // add dynamic params
11749             hiddens = [];
11750             ps = Roo.urlDecode(ps, false);
11751             for(var k in ps){
11752                 if(ps.hasOwnProperty(k)){
11753                     hd = document.createElement('input');
11754                     hd.type = 'hidden';
11755                     hd.name = k;
11756                     hd.value = ps[k];
11757                     form.appendChild(hd);
11758                     hiddens.push(hd);
11759                 }
11760             }
11761         }
11762
11763         function cb(){
11764             var r = {  // bogus response object
11765                 responseText : '',
11766                 responseXML : null
11767             };
11768
11769             r.argument = o ? o.argument : null;
11770
11771             try { //
11772                 var doc;
11773                 if(Roo.isIE){
11774                     doc = frame.contentWindow.document;
11775                 }else {
11776                     doc = (frame.contentDocument || window.frames[id].document);
11777                 }
11778                 if(doc && doc.body){
11779                     r.responseText = doc.body.innerHTML;
11780                 }
11781                 if(doc && doc.XMLDocument){
11782                     r.responseXML = doc.XMLDocument;
11783                 }else {
11784                     r.responseXML = doc;
11785                 }
11786             }
11787             catch(e) {
11788                 // ignore
11789             }
11790
11791             Roo.EventManager.removeListener(frame, 'load', cb, this);
11792
11793             this.fireEvent("requestcomplete", this, r, o);
11794             Roo.callback(o.success, o.scope, [r, o]);
11795             Roo.callback(o.callback, o.scope, [o, true, r]);
11796
11797             setTimeout(function(){document.body.removeChild(frame);}, 100);
11798         }
11799
11800         Roo.EventManager.on(frame, 'load', cb, this);
11801         form.submit();
11802
11803         if(hiddens){ // remove dynamic params
11804             for(var i = 0, len = hiddens.length; i < len; i++){
11805                 form.removeChild(hiddens[i]);
11806             }
11807         }
11808     },
11809     // this is a 'formdata version???'
11810     
11811     
11812     doFormDataUpload : function(o, ps, url)
11813     {
11814         var formData;
11815         if (o.form) {
11816             var form =  Roo.getDom(o.form);
11817             form.enctype = form.encoding = 'multipart/form-data';
11818             formData = o.formData === true ? new FormData(form) : o.formData;
11819         } else {
11820             formData = o.formData === true ? new FormData() : o.formData;
11821         }
11822         
11823       
11824         var cb = {
11825             success: this.handleResponse,
11826             failure: this.handleFailure,
11827             scope: this,
11828             argument: {options: o},
11829             timeout : o.timeout || this.timeout
11830         };
11831  
11832         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11833             if(o.autoAbort){
11834                 this.abort();
11835             }
11836         }else if(this.autoAbort !== false){
11837             this.abort();
11838         }
11839
11840         //Roo.lib.Ajax.defaultPostHeader = null;
11841         Roo.lib.Ajax.useDefaultHeader = false;
11842         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11843         Roo.lib.Ajax.useDefaultHeader = true;
11844  
11845          
11846     }
11847     
11848 });
11849 /*
11850  * Based on:
11851  * Ext JS Library 1.1.1
11852  * Copyright(c) 2006-2007, Ext JS, LLC.
11853  *
11854  * Originally Released Under LGPL - original licence link has changed is not relivant.
11855  *
11856  * Fork - LGPL
11857  * <script type="text/javascript">
11858  */
11859  
11860 /**
11861  * Global Ajax request class.
11862  * 
11863  * @class Roo.Ajax
11864  * @extends Roo.data.Connection
11865  * @static
11866  * 
11867  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11868  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11869  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11870  * @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)
11871  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11872  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11873  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11874  */
11875 Roo.Ajax = new Roo.data.Connection({
11876     // fix up the docs
11877     /**
11878      * @scope Roo.Ajax
11879      * @type {Boolear} 
11880      */
11881     autoAbort : false,
11882
11883     /**
11884      * Serialize the passed form into a url encoded string
11885      * @scope Roo.Ajax
11886      * @param {String/HTMLElement} form
11887      * @return {String}
11888      */
11889     serializeForm : function(form){
11890         return Roo.lib.Ajax.serializeForm(form);
11891     }
11892 });/*
11893  * Based on:
11894  * Ext JS Library 1.1.1
11895  * Copyright(c) 2006-2007, Ext JS, LLC.
11896  *
11897  * Originally Released Under LGPL - original licence link has changed is not relivant.
11898  *
11899  * Fork - LGPL
11900  * <script type="text/javascript">
11901  */
11902
11903  
11904 /**
11905  * @class Roo.UpdateManager
11906  * @extends Roo.util.Observable
11907  * Provides AJAX-style update for Element object.<br><br>
11908  * Usage:<br>
11909  * <pre><code>
11910  * // Get it from a Roo.Element object
11911  * var el = Roo.get("foo");
11912  * var mgr = el.getUpdateManager();
11913  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11914  * ...
11915  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11916  * <br>
11917  * // or directly (returns the same UpdateManager instance)
11918  * var mgr = new Roo.UpdateManager("myElementId");
11919  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11920  * mgr.on("update", myFcnNeedsToKnow);
11921  * <br>
11922    // short handed call directly from the element object
11923    Roo.get("foo").load({
11924         url: "bar.php",
11925         scripts:true,
11926         params: "for=bar",
11927         text: "Loading Foo..."
11928    });
11929  * </code></pre>
11930  * @constructor
11931  * Create new UpdateManager directly.
11932  * @param {String/HTMLElement/Roo.Element} el The element to update
11933  * @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).
11934  */
11935 Roo.UpdateManager = function(el, forceNew){
11936     el = Roo.get(el);
11937     if(!forceNew && el.updateManager){
11938         return el.updateManager;
11939     }
11940     /**
11941      * The Element object
11942      * @type Roo.Element
11943      */
11944     this.el = el;
11945     /**
11946      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11947      * @type String
11948      */
11949     this.defaultUrl = null;
11950
11951     this.addEvents({
11952         /**
11953          * @event beforeupdate
11954          * Fired before an update is made, return false from your handler and the update is cancelled.
11955          * @param {Roo.Element} el
11956          * @param {String/Object/Function} url
11957          * @param {String/Object} params
11958          */
11959         "beforeupdate": true,
11960         /**
11961          * @event update
11962          * Fired after successful update is made.
11963          * @param {Roo.Element} el
11964          * @param {Object} oResponseObject The response Object
11965          */
11966         "update": true,
11967         /**
11968          * @event failure
11969          * Fired on update failure.
11970          * @param {Roo.Element} el
11971          * @param {Object} oResponseObject The response Object
11972          */
11973         "failure": true
11974     });
11975     var d = Roo.UpdateManager.defaults;
11976     /**
11977      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11978      * @type String
11979      */
11980     this.sslBlankUrl = d.sslBlankUrl;
11981     /**
11982      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11983      * @type Boolean
11984      */
11985     this.disableCaching = d.disableCaching;
11986     /**
11987      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11988      * @type String
11989      */
11990     this.indicatorText = d.indicatorText;
11991     /**
11992      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11993      * @type String
11994      */
11995     this.showLoadIndicator = d.showLoadIndicator;
11996     /**
11997      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11998      * @type Number
11999      */
12000     this.timeout = d.timeout;
12001
12002     /**
12003      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12004      * @type Boolean
12005      */
12006     this.loadScripts = d.loadScripts;
12007
12008     /**
12009      * Transaction object of current executing transaction
12010      */
12011     this.transaction = null;
12012
12013     /**
12014      * @private
12015      */
12016     this.autoRefreshProcId = null;
12017     /**
12018      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12019      * @type Function
12020      */
12021     this.refreshDelegate = this.refresh.createDelegate(this);
12022     /**
12023      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12024      * @type Function
12025      */
12026     this.updateDelegate = this.update.createDelegate(this);
12027     /**
12028      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12029      * @type Function
12030      */
12031     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12032     /**
12033      * @private
12034      */
12035     this.successDelegate = this.processSuccess.createDelegate(this);
12036     /**
12037      * @private
12038      */
12039     this.failureDelegate = this.processFailure.createDelegate(this);
12040
12041     if(!this.renderer){
12042      /**
12043       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12044       */
12045     this.renderer = new Roo.UpdateManager.BasicRenderer();
12046     }
12047     
12048     Roo.UpdateManager.superclass.constructor.call(this);
12049 };
12050
12051 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12052     /**
12053      * Get the Element this UpdateManager is bound to
12054      * @return {Roo.Element} The element
12055      */
12056     getEl : function(){
12057         return this.el;
12058     },
12059     /**
12060      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12061      * @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:
12062 <pre><code>
12063 um.update({<br/>
12064     url: "your-url.php",<br/>
12065     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12066     callback: yourFunction,<br/>
12067     scope: yourObject, //(optional scope)  <br/>
12068     discardUrl: false, <br/>
12069     nocache: false,<br/>
12070     text: "Loading...",<br/>
12071     timeout: 30,<br/>
12072     scripts: false<br/>
12073 });
12074 </code></pre>
12075      * The only required property is url. The optional properties nocache, text and scripts
12076      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12077      * @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}
12078      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12079      * @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.
12080      */
12081     update : function(url, params, callback, discardUrl){
12082         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12083             var method = this.method,
12084                 cfg;
12085             if(typeof url == "object"){ // must be config object
12086                 cfg = url;
12087                 url = cfg.url;
12088                 params = params || cfg.params;
12089                 callback = callback || cfg.callback;
12090                 discardUrl = discardUrl || cfg.discardUrl;
12091                 if(callback && cfg.scope){
12092                     callback = callback.createDelegate(cfg.scope);
12093                 }
12094                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12095                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12096                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12097                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12098                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12099             }
12100             this.showLoading();
12101             if(!discardUrl){
12102                 this.defaultUrl = url;
12103             }
12104             if(typeof url == "function"){
12105                 url = url.call(this);
12106             }
12107
12108             method = method || (params ? "POST" : "GET");
12109             if(method == "GET"){
12110                 url = this.prepareUrl(url);
12111             }
12112
12113             var o = Roo.apply(cfg ||{}, {
12114                 url : url,
12115                 params: params,
12116                 success: this.successDelegate,
12117                 failure: this.failureDelegate,
12118                 callback: undefined,
12119                 timeout: (this.timeout*1000),
12120                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12121             });
12122             Roo.log("updated manager called with timeout of " + o.timeout);
12123             this.transaction = Roo.Ajax.request(o);
12124         }
12125     },
12126
12127     /**
12128      * 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.
12129      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12130      * @param {String/HTMLElement} form The form Id or form element
12131      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12132      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12133      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12134      */
12135     formUpdate : function(form, url, reset, callback){
12136         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12137             if(typeof url == "function"){
12138                 url = url.call(this);
12139             }
12140             form = Roo.getDom(form);
12141             this.transaction = Roo.Ajax.request({
12142                 form: form,
12143                 url:url,
12144                 success: this.successDelegate,
12145                 failure: this.failureDelegate,
12146                 timeout: (this.timeout*1000),
12147                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12148             });
12149             this.showLoading.defer(1, this);
12150         }
12151     },
12152
12153     /**
12154      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12155      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12156      */
12157     refresh : function(callback){
12158         if(this.defaultUrl == null){
12159             return;
12160         }
12161         this.update(this.defaultUrl, null, callback, true);
12162     },
12163
12164     /**
12165      * Set this element to auto refresh.
12166      * @param {Number} interval How often to update (in seconds).
12167      * @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)
12168      * @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}
12169      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12170      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12171      */
12172     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12173         if(refreshNow){
12174             this.update(url || this.defaultUrl, params, callback, true);
12175         }
12176         if(this.autoRefreshProcId){
12177             clearInterval(this.autoRefreshProcId);
12178         }
12179         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12180     },
12181
12182     /**
12183      * Stop auto refresh on this element.
12184      */
12185      stopAutoRefresh : function(){
12186         if(this.autoRefreshProcId){
12187             clearInterval(this.autoRefreshProcId);
12188             delete this.autoRefreshProcId;
12189         }
12190     },
12191
12192     isAutoRefreshing : function(){
12193        return this.autoRefreshProcId ? true : false;
12194     },
12195     /**
12196      * Called to update the element to "Loading" state. Override to perform custom action.
12197      */
12198     showLoading : function(){
12199         if(this.showLoadIndicator){
12200             this.el.update(this.indicatorText);
12201         }
12202     },
12203
12204     /**
12205      * Adds unique parameter to query string if disableCaching = true
12206      * @private
12207      */
12208     prepareUrl : function(url){
12209         if(this.disableCaching){
12210             var append = "_dc=" + (new Date().getTime());
12211             if(url.indexOf("?") !== -1){
12212                 url += "&" + append;
12213             }else{
12214                 url += "?" + append;
12215             }
12216         }
12217         return url;
12218     },
12219
12220     /**
12221      * @private
12222      */
12223     processSuccess : function(response){
12224         this.transaction = null;
12225         if(response.argument.form && response.argument.reset){
12226             try{ // put in try/catch since some older FF releases had problems with this
12227                 response.argument.form.reset();
12228             }catch(e){}
12229         }
12230         if(this.loadScripts){
12231             this.renderer.render(this.el, response, this,
12232                 this.updateComplete.createDelegate(this, [response]));
12233         }else{
12234             this.renderer.render(this.el, response, this);
12235             this.updateComplete(response);
12236         }
12237     },
12238
12239     updateComplete : function(response){
12240         this.fireEvent("update", this.el, response);
12241         if(typeof response.argument.callback == "function"){
12242             response.argument.callback(this.el, true, response);
12243         }
12244     },
12245
12246     /**
12247      * @private
12248      */
12249     processFailure : function(response){
12250         this.transaction = null;
12251         this.fireEvent("failure", this.el, response);
12252         if(typeof response.argument.callback == "function"){
12253             response.argument.callback(this.el, false, response);
12254         }
12255     },
12256
12257     /**
12258      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12259      * @param {Object} renderer The object implementing the render() method
12260      */
12261     setRenderer : function(renderer){
12262         this.renderer = renderer;
12263     },
12264
12265     getRenderer : function(){
12266        return this.renderer;
12267     },
12268
12269     /**
12270      * Set the defaultUrl used for updates
12271      * @param {String/Function} defaultUrl The url or a function to call to get the url
12272      */
12273     setDefaultUrl : function(defaultUrl){
12274         this.defaultUrl = defaultUrl;
12275     },
12276
12277     /**
12278      * Aborts the executing transaction
12279      */
12280     abort : function(){
12281         if(this.transaction){
12282             Roo.Ajax.abort(this.transaction);
12283         }
12284     },
12285
12286     /**
12287      * Returns true if an update is in progress
12288      * @return {Boolean}
12289      */
12290     isUpdating : function(){
12291         if(this.transaction){
12292             return Roo.Ajax.isLoading(this.transaction);
12293         }
12294         return false;
12295     }
12296 });
12297
12298 /**
12299  * @class Roo.UpdateManager.defaults
12300  * @static (not really - but it helps the doc tool)
12301  * The defaults collection enables customizing the default properties of UpdateManager
12302  */
12303    Roo.UpdateManager.defaults = {
12304        /**
12305          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12306          * @type Number
12307          */
12308          timeout : 30,
12309
12310          /**
12311          * True to process scripts by default (Defaults to false).
12312          * @type Boolean
12313          */
12314         loadScripts : false,
12315
12316         /**
12317         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12318         * @type String
12319         */
12320         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12321         /**
12322          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12323          * @type Boolean
12324          */
12325         disableCaching : false,
12326         /**
12327          * Whether to show indicatorText when loading (Defaults to true).
12328          * @type Boolean
12329          */
12330         showLoadIndicator : true,
12331         /**
12332          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12333          * @type String
12334          */
12335         indicatorText : '<div class="loading-indicator">Loading...</div>'
12336    };
12337
12338 /**
12339  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12340  *Usage:
12341  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12342  * @param {String/HTMLElement/Roo.Element} el The element to update
12343  * @param {String} url The url
12344  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12345  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12346  * @static
12347  * @deprecated
12348  * @member Roo.UpdateManager
12349  */
12350 Roo.UpdateManager.updateElement = function(el, url, params, options){
12351     var um = Roo.get(el, true).getUpdateManager();
12352     Roo.apply(um, options);
12353     um.update(url, params, options ? options.callback : null);
12354 };
12355 // alias for backwards compat
12356 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12357 /**
12358  * @class Roo.UpdateManager.BasicRenderer
12359  * Default Content renderer. Updates the elements innerHTML with the responseText.
12360  */
12361 Roo.UpdateManager.BasicRenderer = function(){};
12362
12363 Roo.UpdateManager.BasicRenderer.prototype = {
12364     /**
12365      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12366      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12367      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12368      * @param {Roo.Element} el The element being rendered
12369      * @param {Object} response The YUI Connect response object
12370      * @param {UpdateManager} updateManager The calling update manager
12371      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12372      */
12373      render : function(el, response, updateManager, callback){
12374         el.update(response.responseText, updateManager.loadScripts, callback);
12375     }
12376 };
12377 /*
12378  * Based on:
12379  * Roo JS
12380  * (c)) Alan Knowles
12381  * Licence : LGPL
12382  */
12383
12384
12385 /**
12386  * @class Roo.DomTemplate
12387  * @extends Roo.Template
12388  * An effort at a dom based template engine..
12389  *
12390  * Similar to XTemplate, except it uses dom parsing to create the template..
12391  *
12392  * Supported features:
12393  *
12394  *  Tags:
12395
12396 <pre><code>
12397       {a_variable} - output encoded.
12398       {a_variable.format:("Y-m-d")} - call a method on the variable
12399       {a_variable:raw} - unencoded output
12400       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12401       {a_variable:this.method_on_template(...)} - call a method on the template object.
12402  
12403 </code></pre>
12404  *  The tpl tag:
12405 <pre><code>
12406         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12407         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12408         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12409         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12410   
12411 </code></pre>
12412  *      
12413  */
12414 Roo.DomTemplate = function()
12415 {
12416      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12417      if (this.html) {
12418         this.compile();
12419      }
12420 };
12421
12422
12423 Roo.extend(Roo.DomTemplate, Roo.Template, {
12424     /**
12425      * id counter for sub templates.
12426      */
12427     id : 0,
12428     /**
12429      * flag to indicate if dom parser is inside a pre,
12430      * it will strip whitespace if not.
12431      */
12432     inPre : false,
12433     
12434     /**
12435      * The various sub templates
12436      */
12437     tpls : false,
12438     
12439     
12440     
12441     /**
12442      *
12443      * basic tag replacing syntax
12444      * WORD:WORD()
12445      *
12446      * // you can fake an object call by doing this
12447      *  x.t:(test,tesT) 
12448      * 
12449      */
12450     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12451     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12452     
12453     iterChild : function (node, method) {
12454         
12455         var oldPre = this.inPre;
12456         if (node.tagName == 'PRE') {
12457             this.inPre = true;
12458         }
12459         for( var i = 0; i < node.childNodes.length; i++) {
12460             method.call(this, node.childNodes[i]);
12461         }
12462         this.inPre = oldPre;
12463     },
12464     
12465     
12466     
12467     /**
12468      * compile the template
12469      *
12470      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12471      *
12472      */
12473     compile: function()
12474     {
12475         var s = this.html;
12476         
12477         // covert the html into DOM...
12478         var doc = false;
12479         var div =false;
12480         try {
12481             doc = document.implementation.createHTMLDocument("");
12482             doc.documentElement.innerHTML =   this.html  ;
12483             div = doc.documentElement;
12484         } catch (e) {
12485             // old IE... - nasty -- it causes all sorts of issues.. with
12486             // images getting pulled from server..
12487             div = document.createElement('div');
12488             div.innerHTML = this.html;
12489         }
12490         //doc.documentElement.innerHTML = htmlBody
12491          
12492         
12493         
12494         this.tpls = [];
12495         var _t = this;
12496         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12497         
12498         var tpls = this.tpls;
12499         
12500         // create a top level template from the snippet..
12501         
12502         //Roo.log(div.innerHTML);
12503         
12504         var tpl = {
12505             uid : 'master',
12506             id : this.id++,
12507             attr : false,
12508             value : false,
12509             body : div.innerHTML,
12510             
12511             forCall : false,
12512             execCall : false,
12513             dom : div,
12514             isTop : true
12515             
12516         };
12517         tpls.unshift(tpl);
12518         
12519         
12520         // compile them...
12521         this.tpls = [];
12522         Roo.each(tpls, function(tp){
12523             this.compileTpl(tp);
12524             this.tpls[tp.id] = tp;
12525         }, this);
12526         
12527         this.master = tpls[0];
12528         return this;
12529         
12530         
12531     },
12532     
12533     compileNode : function(node, istop) {
12534         // test for
12535         //Roo.log(node);
12536         
12537         
12538         // skip anything not a tag..
12539         if (node.nodeType != 1) {
12540             if (node.nodeType == 3 && !this.inPre) {
12541                 // reduce white space..
12542                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12543                 
12544             }
12545             return;
12546         }
12547         
12548         var tpl = {
12549             uid : false,
12550             id : false,
12551             attr : false,
12552             value : false,
12553             body : '',
12554             
12555             forCall : false,
12556             execCall : false,
12557             dom : false,
12558             isTop : istop
12559             
12560             
12561         };
12562         
12563         
12564         switch(true) {
12565             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12566             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12567             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12568             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12569             // no default..
12570         }
12571         
12572         
12573         if (!tpl.attr) {
12574             // just itterate children..
12575             this.iterChild(node,this.compileNode);
12576             return;
12577         }
12578         tpl.uid = this.id++;
12579         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12580         node.removeAttribute('roo-'+ tpl.attr);
12581         if (tpl.attr != 'name') {
12582             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12583             node.parentNode.replaceChild(placeholder,  node);
12584         } else {
12585             
12586             var placeholder =  document.createElement('span');
12587             placeholder.className = 'roo-tpl-' + tpl.value;
12588             node.parentNode.replaceChild(placeholder,  node);
12589         }
12590         
12591         // parent now sees '{domtplXXXX}
12592         this.iterChild(node,this.compileNode);
12593         
12594         // we should now have node body...
12595         var div = document.createElement('div');
12596         div.appendChild(node);
12597         tpl.dom = node;
12598         // this has the unfortunate side effect of converting tagged attributes
12599         // eg. href="{...}" into %7C...%7D
12600         // this has been fixed by searching for those combo's although it's a bit hacky..
12601         
12602         
12603         tpl.body = div.innerHTML;
12604         
12605         
12606          
12607         tpl.id = tpl.uid;
12608         switch(tpl.attr) {
12609             case 'for' :
12610                 switch (tpl.value) {
12611                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12612                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12613                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12614                 }
12615                 break;
12616             
12617             case 'exec':
12618                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12619                 break;
12620             
12621             case 'if':     
12622                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12623                 break;
12624             
12625             case 'name':
12626                 tpl.id  = tpl.value; // replace non characters???
12627                 break;
12628             
12629         }
12630         
12631         
12632         this.tpls.push(tpl);
12633         
12634         
12635         
12636     },
12637     
12638     
12639     
12640     
12641     /**
12642      * Compile a segment of the template into a 'sub-template'
12643      *
12644      * 
12645      * 
12646      *
12647      */
12648     compileTpl : function(tpl)
12649     {
12650         var fm = Roo.util.Format;
12651         var useF = this.disableFormats !== true;
12652         
12653         var sep = Roo.isGecko ? "+\n" : ",\n";
12654         
12655         var undef = function(str) {
12656             Roo.debug && Roo.log("Property not found :"  + str);
12657             return '';
12658         };
12659           
12660         //Roo.log(tpl.body);
12661         
12662         
12663         
12664         var fn = function(m, lbrace, name, format, args)
12665         {
12666             //Roo.log("ARGS");
12667             //Roo.log(arguments);
12668             args = args ? args.replace(/\\'/g,"'") : args;
12669             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12670             if (typeof(format) == 'undefined') {
12671                 format =  'htmlEncode'; 
12672             }
12673             if (format == 'raw' ) {
12674                 format = false;
12675             }
12676             
12677             if(name.substr(0, 6) == 'domtpl'){
12678                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12679             }
12680             
12681             // build an array of options to determine if value is undefined..
12682             
12683             // basically get 'xxxx.yyyy' then do
12684             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12685             //    (function () { Roo.log("Property not found"); return ''; })() :
12686             //    ......
12687             
12688             var udef_ar = [];
12689             var lookfor = '';
12690             Roo.each(name.split('.'), function(st) {
12691                 lookfor += (lookfor.length ? '.': '') + st;
12692                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12693             });
12694             
12695             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12696             
12697             
12698             if(format && useF){
12699                 
12700                 args = args ? ',' + args : "";
12701                  
12702                 if(format.substr(0, 5) != "this."){
12703                     format = "fm." + format + '(';
12704                 }else{
12705                     format = 'this.call("'+ format.substr(5) + '", ';
12706                     args = ", values";
12707                 }
12708                 
12709                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12710             }
12711              
12712             if (args && args.length) {
12713                 // called with xxyx.yuu:(test,test)
12714                 // change to ()
12715                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12716             }
12717             // raw.. - :raw modifier..
12718             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12719             
12720         };
12721         var body;
12722         // branched to use + in gecko and [].join() in others
12723         if(Roo.isGecko){
12724             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12725                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12726                     "';};};";
12727         }else{
12728             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12729             body.push(tpl.body.replace(/(\r\n|\n)/g,
12730                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12731             body.push("'].join('');};};");
12732             body = body.join('');
12733         }
12734         
12735         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12736        
12737         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12738         eval(body);
12739         
12740         return this;
12741     },
12742      
12743     /**
12744      * same as applyTemplate, except it's done to one of the subTemplates
12745      * when using named templates, you can do:
12746      *
12747      * var str = pl.applySubTemplate('your-name', values);
12748      *
12749      * 
12750      * @param {Number} id of the template
12751      * @param {Object} values to apply to template
12752      * @param {Object} parent (normaly the instance of this object)
12753      */
12754     applySubTemplate : function(id, values, parent)
12755     {
12756         
12757         
12758         var t = this.tpls[id];
12759         
12760         
12761         try { 
12762             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12763                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12764                 return '';
12765             }
12766         } catch(e) {
12767             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12768             Roo.log(values);
12769           
12770             return '';
12771         }
12772         try { 
12773             
12774             if(t.execCall && t.execCall.call(this, values, parent)){
12775                 return '';
12776             }
12777         } catch(e) {
12778             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12779             Roo.log(values);
12780             return '';
12781         }
12782         
12783         try {
12784             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12785             parent = t.target ? values : parent;
12786             if(t.forCall && vs instanceof Array){
12787                 var buf = [];
12788                 for(var i = 0, len = vs.length; i < len; i++){
12789                     try {
12790                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12791                     } catch (e) {
12792                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12793                         Roo.log(e.body);
12794                         //Roo.log(t.compiled);
12795                         Roo.log(vs[i]);
12796                     }   
12797                 }
12798                 return buf.join('');
12799             }
12800         } catch (e) {
12801             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12802             Roo.log(values);
12803             return '';
12804         }
12805         try {
12806             return t.compiled.call(this, vs, parent);
12807         } catch (e) {
12808             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12809             Roo.log(e.body);
12810             //Roo.log(t.compiled);
12811             Roo.log(values);
12812             return '';
12813         }
12814     },
12815
12816    
12817
12818     applyTemplate : function(values){
12819         return this.master.compiled.call(this, values, {});
12820         //var s = this.subs;
12821     },
12822
12823     apply : function(){
12824         return this.applyTemplate.apply(this, arguments);
12825     }
12826
12827  });
12828
12829 Roo.DomTemplate.from = function(el){
12830     el = Roo.getDom(el);
12831     return new Roo.Domtemplate(el.value || el.innerHTML);
12832 };/*
12833  * Based on:
12834  * Ext JS Library 1.1.1
12835  * Copyright(c) 2006-2007, Ext JS, LLC.
12836  *
12837  * Originally Released Under LGPL - original licence link has changed is not relivant.
12838  *
12839  * Fork - LGPL
12840  * <script type="text/javascript">
12841  */
12842
12843 /**
12844  * @class Roo.util.DelayedTask
12845  * Provides a convenient method of performing setTimeout where a new
12846  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12847  * You can use this class to buffer
12848  * the keypress events for a certain number of milliseconds, and perform only if they stop
12849  * for that amount of time.
12850  * @constructor The parameters to this constructor serve as defaults and are not required.
12851  * @param {Function} fn (optional) The default function to timeout
12852  * @param {Object} scope (optional) The default scope of that timeout
12853  * @param {Array} args (optional) The default Array of arguments
12854  */
12855 Roo.util.DelayedTask = function(fn, scope, args){
12856     var id = null, d, t;
12857
12858     var call = function(){
12859         var now = new Date().getTime();
12860         if(now - t >= d){
12861             clearInterval(id);
12862             id = null;
12863             fn.apply(scope, args || []);
12864         }
12865     };
12866     /**
12867      * Cancels any pending timeout and queues a new one
12868      * @param {Number} delay The milliseconds to delay
12869      * @param {Function} newFn (optional) Overrides function passed to constructor
12870      * @param {Object} newScope (optional) Overrides scope passed to constructor
12871      * @param {Array} newArgs (optional) Overrides args passed to constructor
12872      */
12873     this.delay = function(delay, newFn, newScope, newArgs){
12874         if(id && delay != d){
12875             this.cancel();
12876         }
12877         d = delay;
12878         t = new Date().getTime();
12879         fn = newFn || fn;
12880         scope = newScope || scope;
12881         args = newArgs || args;
12882         if(!id){
12883             id = setInterval(call, d);
12884         }
12885     };
12886
12887     /**
12888      * Cancel the last queued timeout
12889      */
12890     this.cancel = function(){
12891         if(id){
12892             clearInterval(id);
12893             id = null;
12894         }
12895     };
12896 };/*
12897  * Based on:
12898  * Ext JS Library 1.1.1
12899  * Copyright(c) 2006-2007, Ext JS, LLC.
12900  *
12901  * Originally Released Under LGPL - original licence link has changed is not relivant.
12902  *
12903  * Fork - LGPL
12904  * <script type="text/javascript">
12905  */
12906  
12907  
12908 Roo.util.TaskRunner = function(interval){
12909     interval = interval || 10;
12910     var tasks = [], removeQueue = [];
12911     var id = 0;
12912     var running = false;
12913
12914     var stopThread = function(){
12915         running = false;
12916         clearInterval(id);
12917         id = 0;
12918     };
12919
12920     var startThread = function(){
12921         if(!running){
12922             running = true;
12923             id = setInterval(runTasks, interval);
12924         }
12925     };
12926
12927     var removeTask = function(task){
12928         removeQueue.push(task);
12929         if(task.onStop){
12930             task.onStop();
12931         }
12932     };
12933
12934     var runTasks = function(){
12935         if(removeQueue.length > 0){
12936             for(var i = 0, len = removeQueue.length; i < len; i++){
12937                 tasks.remove(removeQueue[i]);
12938             }
12939             removeQueue = [];
12940             if(tasks.length < 1){
12941                 stopThread();
12942                 return;
12943             }
12944         }
12945         var now = new Date().getTime();
12946         for(var i = 0, len = tasks.length; i < len; ++i){
12947             var t = tasks[i];
12948             var itime = now - t.taskRunTime;
12949             if(t.interval <= itime){
12950                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12951                 t.taskRunTime = now;
12952                 if(rt === false || t.taskRunCount === t.repeat){
12953                     removeTask(t);
12954                     return;
12955                 }
12956             }
12957             if(t.duration && t.duration <= (now - t.taskStartTime)){
12958                 removeTask(t);
12959             }
12960         }
12961     };
12962
12963     /**
12964      * Queues a new task.
12965      * @param {Object} task
12966      */
12967     this.start = function(task){
12968         tasks.push(task);
12969         task.taskStartTime = new Date().getTime();
12970         task.taskRunTime = 0;
12971         task.taskRunCount = 0;
12972         startThread();
12973         return task;
12974     };
12975
12976     this.stop = function(task){
12977         removeTask(task);
12978         return task;
12979     };
12980
12981     this.stopAll = function(){
12982         stopThread();
12983         for(var i = 0, len = tasks.length; i < len; i++){
12984             if(tasks[i].onStop){
12985                 tasks[i].onStop();
12986             }
12987         }
12988         tasks = [];
12989         removeQueue = [];
12990     };
12991 };
12992
12993 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12994  * Based on:
12995  * Ext JS Library 1.1.1
12996  * Copyright(c) 2006-2007, Ext JS, LLC.
12997  *
12998  * Originally Released Under LGPL - original licence link has changed is not relivant.
12999  *
13000  * Fork - LGPL
13001  * <script type="text/javascript">
13002  */
13003
13004  
13005 /**
13006  * @class Roo.util.MixedCollection
13007  * @extends Roo.util.Observable
13008  * A Collection class that maintains both numeric indexes and keys and exposes events.
13009  * @constructor
13010  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13011  * collection (defaults to false)
13012  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13013  * and return the key value for that item.  This is used when available to look up the key on items that
13014  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13015  * equivalent to providing an implementation for the {@link #getKey} method.
13016  */
13017 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13018     this.items = [];
13019     this.map = {};
13020     this.keys = [];
13021     this.length = 0;
13022     this.addEvents({
13023         /**
13024          * @event clear
13025          * Fires when the collection is cleared.
13026          */
13027         "clear" : true,
13028         /**
13029          * @event add
13030          * Fires when an item is added to the collection.
13031          * @param {Number} index The index at which the item was added.
13032          * @param {Object} o The item added.
13033          * @param {String} key The key associated with the added item.
13034          */
13035         "add" : true,
13036         /**
13037          * @event replace
13038          * Fires when an item is replaced in the collection.
13039          * @param {String} key he key associated with the new added.
13040          * @param {Object} old The item being replaced.
13041          * @param {Object} new The new item.
13042          */
13043         "replace" : true,
13044         /**
13045          * @event remove
13046          * Fires when an item is removed from the collection.
13047          * @param {Object} o The item being removed.
13048          * @param {String} key (optional) The key associated with the removed item.
13049          */
13050         "remove" : true,
13051         "sort" : true
13052     });
13053     this.allowFunctions = allowFunctions === true;
13054     if(keyFn){
13055         this.getKey = keyFn;
13056     }
13057     Roo.util.MixedCollection.superclass.constructor.call(this);
13058 };
13059
13060 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13061     allowFunctions : false,
13062     
13063 /**
13064  * Adds an item to the collection.
13065  * @param {String} key The key to associate with the item
13066  * @param {Object} o The item to add.
13067  * @return {Object} The item added.
13068  */
13069     add : function(key, o){
13070         if(arguments.length == 1){
13071             o = arguments[0];
13072             key = this.getKey(o);
13073         }
13074         if(typeof key == "undefined" || key === null){
13075             this.length++;
13076             this.items.push(o);
13077             this.keys.push(null);
13078         }else{
13079             var old = this.map[key];
13080             if(old){
13081                 return this.replace(key, o);
13082             }
13083             this.length++;
13084             this.items.push(o);
13085             this.map[key] = o;
13086             this.keys.push(key);
13087         }
13088         this.fireEvent("add", this.length-1, o, key);
13089         return o;
13090     },
13091        
13092 /**
13093   * MixedCollection has a generic way to fetch keys if you implement getKey.
13094 <pre><code>
13095 // normal way
13096 var mc = new Roo.util.MixedCollection();
13097 mc.add(someEl.dom.id, someEl);
13098 mc.add(otherEl.dom.id, otherEl);
13099 //and so on
13100
13101 // using getKey
13102 var mc = new Roo.util.MixedCollection();
13103 mc.getKey = function(el){
13104    return el.dom.id;
13105 };
13106 mc.add(someEl);
13107 mc.add(otherEl);
13108
13109 // or via the constructor
13110 var mc = new Roo.util.MixedCollection(false, function(el){
13111    return el.dom.id;
13112 });
13113 mc.add(someEl);
13114 mc.add(otherEl);
13115 </code></pre>
13116  * @param o {Object} The item for which to find the key.
13117  * @return {Object} The key for the passed item.
13118  */
13119     getKey : function(o){
13120          return o.id; 
13121     },
13122    
13123 /**
13124  * Replaces an item in the collection.
13125  * @param {String} key The key associated with the item to replace, or the item to replace.
13126  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13127  * @return {Object}  The new item.
13128  */
13129     replace : function(key, o){
13130         if(arguments.length == 1){
13131             o = arguments[0];
13132             key = this.getKey(o);
13133         }
13134         var old = this.item(key);
13135         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13136              return this.add(key, o);
13137         }
13138         var index = this.indexOfKey(key);
13139         this.items[index] = o;
13140         this.map[key] = o;
13141         this.fireEvent("replace", key, old, o);
13142         return o;
13143     },
13144    
13145 /**
13146  * Adds all elements of an Array or an Object to the collection.
13147  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13148  * an Array of values, each of which are added to the collection.
13149  */
13150     addAll : function(objs){
13151         if(arguments.length > 1 || objs instanceof Array){
13152             var args = arguments.length > 1 ? arguments : objs;
13153             for(var i = 0, len = args.length; i < len; i++){
13154                 this.add(args[i]);
13155             }
13156         }else{
13157             for(var key in objs){
13158                 if(this.allowFunctions || typeof objs[key] != "function"){
13159                     this.add(key, objs[key]);
13160                 }
13161             }
13162         }
13163     },
13164    
13165 /**
13166  * Executes the specified function once for every item in the collection, passing each
13167  * item as the first and only parameter. returning false from the function will stop the iteration.
13168  * @param {Function} fn The function to execute for each item.
13169  * @param {Object} scope (optional) The scope in which to execute the function.
13170  */
13171     each : function(fn, scope){
13172         var items = [].concat(this.items); // each safe for removal
13173         for(var i = 0, len = items.length; i < len; i++){
13174             if(fn.call(scope || items[i], items[i], i, len) === false){
13175                 break;
13176             }
13177         }
13178     },
13179    
13180 /**
13181  * Executes the specified function once for every key in the collection, passing each
13182  * key, and its associated item as the first two parameters.
13183  * @param {Function} fn The function to execute for each item.
13184  * @param {Object} scope (optional) The scope in which to execute the function.
13185  */
13186     eachKey : function(fn, scope){
13187         for(var i = 0, len = this.keys.length; i < len; i++){
13188             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13189         }
13190     },
13191    
13192 /**
13193  * Returns the first item in the collection which elicits a true return value from the
13194  * passed selection function.
13195  * @param {Function} fn The selection function to execute for each item.
13196  * @param {Object} scope (optional) The scope in which to execute the function.
13197  * @return {Object} The first item in the collection which returned true from the selection function.
13198  */
13199     find : function(fn, scope){
13200         for(var i = 0, len = this.items.length; i < len; i++){
13201             if(fn.call(scope || window, this.items[i], this.keys[i])){
13202                 return this.items[i];
13203             }
13204         }
13205         return null;
13206     },
13207    
13208 /**
13209  * Inserts an item at the specified index in the collection.
13210  * @param {Number} index The index to insert the item at.
13211  * @param {String} key The key to associate with the new item, or the item itself.
13212  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13213  * @return {Object} The item inserted.
13214  */
13215     insert : function(index, key, o){
13216         if(arguments.length == 2){
13217             o = arguments[1];
13218             key = this.getKey(o);
13219         }
13220         if(index >= this.length){
13221             return this.add(key, o);
13222         }
13223         this.length++;
13224         this.items.splice(index, 0, o);
13225         if(typeof key != "undefined" && key != null){
13226             this.map[key] = o;
13227         }
13228         this.keys.splice(index, 0, key);
13229         this.fireEvent("add", index, o, key);
13230         return o;
13231     },
13232    
13233 /**
13234  * Removed an item from the collection.
13235  * @param {Object} o The item to remove.
13236  * @return {Object} The item removed.
13237  */
13238     remove : function(o){
13239         return this.removeAt(this.indexOf(o));
13240     },
13241    
13242 /**
13243  * Remove an item from a specified index in the collection.
13244  * @param {Number} index The index within the collection of the item to remove.
13245  */
13246     removeAt : function(index){
13247         if(index < this.length && index >= 0){
13248             this.length--;
13249             var o = this.items[index];
13250             this.items.splice(index, 1);
13251             var key = this.keys[index];
13252             if(typeof key != "undefined"){
13253                 delete this.map[key];
13254             }
13255             this.keys.splice(index, 1);
13256             this.fireEvent("remove", o, key);
13257         }
13258     },
13259    
13260 /**
13261  * Removed an item associated with the passed key fom the collection.
13262  * @param {String} key The key of the item to remove.
13263  */
13264     removeKey : function(key){
13265         return this.removeAt(this.indexOfKey(key));
13266     },
13267    
13268 /**
13269  * Returns the number of items in the collection.
13270  * @return {Number} the number of items in the collection.
13271  */
13272     getCount : function(){
13273         return this.length; 
13274     },
13275    
13276 /**
13277  * Returns index within the collection of the passed Object.
13278  * @param {Object} o The item to find the index of.
13279  * @return {Number} index of the item.
13280  */
13281     indexOf : function(o){
13282         if(!this.items.indexOf){
13283             for(var i = 0, len = this.items.length; i < len; i++){
13284                 if(this.items[i] == o) {
13285                     return i;
13286                 }
13287             }
13288             return -1;
13289         }else{
13290             return this.items.indexOf(o);
13291         }
13292     },
13293    
13294 /**
13295  * Returns index within the collection of the passed key.
13296  * @param {String} key The key to find the index of.
13297  * @return {Number} index of the key.
13298  */
13299     indexOfKey : function(key){
13300         if(!this.keys.indexOf){
13301             for(var i = 0, len = this.keys.length; i < len; i++){
13302                 if(this.keys[i] == key) {
13303                     return i;
13304                 }
13305             }
13306             return -1;
13307         }else{
13308             return this.keys.indexOf(key);
13309         }
13310     },
13311    
13312 /**
13313  * Returns the item associated with the passed key OR index. Key has priority over index.
13314  * @param {String/Number} key The key or index of the item.
13315  * @return {Object} The item associated with the passed key.
13316  */
13317     item : function(key){
13318         if (key === 'length') {
13319             return null;
13320         }
13321         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13322         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13323     },
13324     
13325 /**
13326  * Returns the item at the specified index.
13327  * @param {Number} index The index of the item.
13328  * @return {Object}
13329  */
13330     itemAt : function(index){
13331         return this.items[index];
13332     },
13333     
13334 /**
13335  * Returns the item associated with the passed key.
13336  * @param {String/Number} key The key of the item.
13337  * @return {Object} The item associated with the passed key.
13338  */
13339     key : function(key){
13340         return this.map[key];
13341     },
13342    
13343 /**
13344  * Returns true if the collection contains the passed Object as an item.
13345  * @param {Object} o  The Object to look for in the collection.
13346  * @return {Boolean} True if the collection contains the Object as an item.
13347  */
13348     contains : function(o){
13349         return this.indexOf(o) != -1;
13350     },
13351    
13352 /**
13353  * Returns true if the collection contains the passed Object as a key.
13354  * @param {String} key The key to look for in the collection.
13355  * @return {Boolean} True if the collection contains the Object as a key.
13356  */
13357     containsKey : function(key){
13358         return typeof this.map[key] != "undefined";
13359     },
13360    
13361 /**
13362  * Removes all items from the collection.
13363  */
13364     clear : function(){
13365         this.length = 0;
13366         this.items = [];
13367         this.keys = [];
13368         this.map = {};
13369         this.fireEvent("clear");
13370     },
13371    
13372 /**
13373  * Returns the first item in the collection.
13374  * @return {Object} the first item in the collection..
13375  */
13376     first : function(){
13377         return this.items[0]; 
13378     },
13379    
13380 /**
13381  * Returns the last item in the collection.
13382  * @return {Object} the last item in the collection..
13383  */
13384     last : function(){
13385         return this.items[this.length-1];   
13386     },
13387     
13388     _sort : function(property, dir, fn){
13389         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13390         fn = fn || function(a, b){
13391             return a-b;
13392         };
13393         var c = [], k = this.keys, items = this.items;
13394         for(var i = 0, len = items.length; i < len; i++){
13395             c[c.length] = {key: k[i], value: items[i], index: i};
13396         }
13397         c.sort(function(a, b){
13398             var v = fn(a[property], b[property]) * dsc;
13399             if(v == 0){
13400                 v = (a.index < b.index ? -1 : 1);
13401             }
13402             return v;
13403         });
13404         for(var i = 0, len = c.length; i < len; i++){
13405             items[i] = c[i].value;
13406             k[i] = c[i].key;
13407         }
13408         this.fireEvent("sort", this);
13409     },
13410     
13411     /**
13412      * Sorts this collection with the passed comparison function
13413      * @param {String} direction (optional) "ASC" or "DESC"
13414      * @param {Function} fn (optional) comparison function
13415      */
13416     sort : function(dir, fn){
13417         this._sort("value", dir, fn);
13418     },
13419     
13420     /**
13421      * Sorts this collection by keys
13422      * @param {String} direction (optional) "ASC" or "DESC"
13423      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13424      */
13425     keySort : function(dir, fn){
13426         this._sort("key", dir, fn || function(a, b){
13427             return String(a).toUpperCase()-String(b).toUpperCase();
13428         });
13429     },
13430     
13431     /**
13432      * Returns a range of items in this collection
13433      * @param {Number} startIndex (optional) defaults to 0
13434      * @param {Number} endIndex (optional) default to the last item
13435      * @return {Array} An array of items
13436      */
13437     getRange : function(start, end){
13438         var items = this.items;
13439         if(items.length < 1){
13440             return [];
13441         }
13442         start = start || 0;
13443         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13444         var r = [];
13445         if(start <= end){
13446             for(var i = start; i <= end; i++) {
13447                     r[r.length] = items[i];
13448             }
13449         }else{
13450             for(var i = start; i >= end; i--) {
13451                     r[r.length] = items[i];
13452             }
13453         }
13454         return r;
13455     },
13456         
13457     /**
13458      * Filter the <i>objects</i> in this collection by a specific property. 
13459      * Returns a new collection that has been filtered.
13460      * @param {String} property A property on your objects
13461      * @param {String/RegExp} value Either string that the property values 
13462      * should start with or a RegExp to test against the property
13463      * @return {MixedCollection} The new filtered collection
13464      */
13465     filter : function(property, value){
13466         if(!value.exec){ // not a regex
13467             value = String(value);
13468             if(value.length == 0){
13469                 return this.clone();
13470             }
13471             value = new RegExp("^" + Roo.escapeRe(value), "i");
13472         }
13473         return this.filterBy(function(o){
13474             return o && value.test(o[property]);
13475         });
13476         },
13477     
13478     /**
13479      * Filter by a function. * Returns a new collection that has been filtered.
13480      * The passed function will be called with each 
13481      * object in the collection. If the function returns true, the value is included 
13482      * otherwise it is filtered.
13483      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13484      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13485      * @return {MixedCollection} The new filtered collection
13486      */
13487     filterBy : function(fn, scope){
13488         var r = new Roo.util.MixedCollection();
13489         r.getKey = this.getKey;
13490         var k = this.keys, it = this.items;
13491         for(var i = 0, len = it.length; i < len; i++){
13492             if(fn.call(scope||this, it[i], k[i])){
13493                                 r.add(k[i], it[i]);
13494                         }
13495         }
13496         return r;
13497     },
13498     
13499     /**
13500      * Creates a duplicate of this collection
13501      * @return {MixedCollection}
13502      */
13503     clone : function(){
13504         var r = new Roo.util.MixedCollection();
13505         var k = this.keys, it = this.items;
13506         for(var i = 0, len = it.length; i < len; i++){
13507             r.add(k[i], it[i]);
13508         }
13509         r.getKey = this.getKey;
13510         return r;
13511     }
13512 });
13513 /**
13514  * Returns the item associated with the passed key or index.
13515  * @method
13516  * @param {String/Number} key The key or index of the item.
13517  * @return {Object} The item associated with the passed key.
13518  */
13519 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13520  * Based on:
13521  * Ext JS Library 1.1.1
13522  * Copyright(c) 2006-2007, Ext JS, LLC.
13523  *
13524  * Originally Released Under LGPL - original licence link has changed is not relivant.
13525  *
13526  * Fork - LGPL
13527  * <script type="text/javascript">
13528  */
13529 /**
13530  * @class Roo.util.JSON
13531  * Modified version of Douglas Crockford"s json.js that doesn"t
13532  * mess with the Object prototype 
13533  * http://www.json.org/js.html
13534  * @singleton
13535  */
13536 Roo.util.JSON = new (function(){
13537     var useHasOwn = {}.hasOwnProperty ? true : false;
13538     
13539     // crashes Safari in some instances
13540     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13541     
13542     var pad = function(n) {
13543         return n < 10 ? "0" + n : n;
13544     };
13545     
13546     var m = {
13547         "\b": '\\b',
13548         "\t": '\\t',
13549         "\n": '\\n',
13550         "\f": '\\f',
13551         "\r": '\\r',
13552         '"' : '\\"',
13553         "\\": '\\\\'
13554     };
13555
13556     var encodeString = function(s){
13557         if (/["\\\x00-\x1f]/.test(s)) {
13558             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13559                 var c = m[b];
13560                 if(c){
13561                     return c;
13562                 }
13563                 c = b.charCodeAt();
13564                 return "\\u00" +
13565                     Math.floor(c / 16).toString(16) +
13566                     (c % 16).toString(16);
13567             }) + '"';
13568         }
13569         return '"' + s + '"';
13570     };
13571     
13572     var encodeArray = function(o){
13573         var a = ["["], b, i, l = o.length, v;
13574             for (i = 0; i < l; i += 1) {
13575                 v = o[i];
13576                 switch (typeof v) {
13577                     case "undefined":
13578                     case "function":
13579                     case "unknown":
13580                         break;
13581                     default:
13582                         if (b) {
13583                             a.push(',');
13584                         }
13585                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13586                         b = true;
13587                 }
13588             }
13589             a.push("]");
13590             return a.join("");
13591     };
13592     
13593     var encodeDate = function(o){
13594         return '"' + o.getFullYear() + "-" +
13595                 pad(o.getMonth() + 1) + "-" +
13596                 pad(o.getDate()) + "T" +
13597                 pad(o.getHours()) + ":" +
13598                 pad(o.getMinutes()) + ":" +
13599                 pad(o.getSeconds()) + '"';
13600     };
13601     
13602     /**
13603      * Encodes an Object, Array or other value
13604      * @param {Mixed} o The variable to encode
13605      * @return {String} The JSON string
13606      */
13607     this.encode = function(o)
13608     {
13609         // should this be extended to fully wrap stringify..
13610         
13611         if(typeof o == "undefined" || o === null){
13612             return "null";
13613         }else if(o instanceof Array){
13614             return encodeArray(o);
13615         }else if(o instanceof Date){
13616             return encodeDate(o);
13617         }else if(typeof o == "string"){
13618             return encodeString(o);
13619         }else if(typeof o == "number"){
13620             return isFinite(o) ? String(o) : "null";
13621         }else if(typeof o == "boolean"){
13622             return String(o);
13623         }else {
13624             var a = ["{"], b, i, v;
13625             for (i in o) {
13626                 if(!useHasOwn || o.hasOwnProperty(i)) {
13627                     v = o[i];
13628                     switch (typeof v) {
13629                     case "undefined":
13630                     case "function":
13631                     case "unknown":
13632                         break;
13633                     default:
13634                         if(b){
13635                             a.push(',');
13636                         }
13637                         a.push(this.encode(i), ":",
13638                                 v === null ? "null" : this.encode(v));
13639                         b = true;
13640                     }
13641                 }
13642             }
13643             a.push("}");
13644             return a.join("");
13645         }
13646     };
13647     
13648     /**
13649      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13650      * @param {String} json The JSON string
13651      * @return {Object} The resulting object
13652      */
13653     this.decode = function(json){
13654         
13655         return  /** eval:var:json */ eval("(" + json + ')');
13656     };
13657 })();
13658 /** 
13659  * Shorthand for {@link Roo.util.JSON#encode}
13660  * @member Roo encode 
13661  * @method */
13662 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13663 /** 
13664  * Shorthand for {@link Roo.util.JSON#decode}
13665  * @member Roo decode 
13666  * @method */
13667 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13668 /*
13669  * Based on:
13670  * Ext JS Library 1.1.1
13671  * Copyright(c) 2006-2007, Ext JS, LLC.
13672  *
13673  * Originally Released Under LGPL - original licence link has changed is not relivant.
13674  *
13675  * Fork - LGPL
13676  * <script type="text/javascript">
13677  */
13678  
13679 /**
13680  * @class Roo.util.Format
13681  * Reusable data formatting functions
13682  * @singleton
13683  */
13684 Roo.util.Format = function(){
13685     var trimRe = /^\s+|\s+$/g;
13686     return {
13687         /**
13688          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13689          * @param {String} value The string to truncate
13690          * @param {Number} length The maximum length to allow before truncating
13691          * @return {String} The converted text
13692          */
13693         ellipsis : function(value, len){
13694             if(value && value.length > len){
13695                 return value.substr(0, len-3)+"...";
13696             }
13697             return value;
13698         },
13699
13700         /**
13701          * Checks a reference and converts it to empty string if it is undefined
13702          * @param {Mixed} value Reference to check
13703          * @return {Mixed} Empty string if converted, otherwise the original value
13704          */
13705         undef : function(value){
13706             return typeof value != "undefined" ? value : "";
13707         },
13708
13709         /**
13710          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13711          * @param {String} value The string to encode
13712          * @return {String} The encoded text
13713          */
13714         htmlEncode : function(value){
13715             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13716         },
13717
13718         /**
13719          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13720          * @param {String} value The string to decode
13721          * @return {String} The decoded text
13722          */
13723         htmlDecode : function(value){
13724             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13725         },
13726
13727         /**
13728          * Trims any whitespace from either side of a string
13729          * @param {String} value The text to trim
13730          * @return {String} The trimmed text
13731          */
13732         trim : function(value){
13733             return String(value).replace(trimRe, "");
13734         },
13735
13736         /**
13737          * Returns a substring from within an original string
13738          * @param {String} value The original text
13739          * @param {Number} start The start index of the substring
13740          * @param {Number} length The length of the substring
13741          * @return {String} The substring
13742          */
13743         substr : function(value, start, length){
13744             return String(value).substr(start, length);
13745         },
13746
13747         /**
13748          * Converts a string to all lower case letters
13749          * @param {String} value The text to convert
13750          * @return {String} The converted text
13751          */
13752         lowercase : function(value){
13753             return String(value).toLowerCase();
13754         },
13755
13756         /**
13757          * Converts a string to all upper case letters
13758          * @param {String} value The text to convert
13759          * @return {String} The converted text
13760          */
13761         uppercase : function(value){
13762             return String(value).toUpperCase();
13763         },
13764
13765         /**
13766          * Converts the first character only of a string to upper case
13767          * @param {String} value The text to convert
13768          * @return {String} The converted text
13769          */
13770         capitalize : function(value){
13771             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13772         },
13773
13774         // private
13775         call : function(value, fn){
13776             if(arguments.length > 2){
13777                 var args = Array.prototype.slice.call(arguments, 2);
13778                 args.unshift(value);
13779                  
13780                 return /** eval:var:value */  eval(fn).apply(window, args);
13781             }else{
13782                 /** eval:var:value */
13783                 return /** eval:var:value */ eval(fn).call(window, value);
13784             }
13785         },
13786
13787        
13788         /**
13789          * safer version of Math.toFixed..??/
13790          * @param {Number/String} value The numeric value to format
13791          * @param {Number/String} value Decimal places 
13792          * @return {String} The formatted currency string
13793          */
13794         toFixed : function(v, n)
13795         {
13796             // why not use to fixed - precision is buggered???
13797             if (!n) {
13798                 return Math.round(v-0);
13799             }
13800             var fact = Math.pow(10,n+1);
13801             v = (Math.round((v-0)*fact))/fact;
13802             var z = (''+fact).substring(2);
13803             if (v == Math.floor(v)) {
13804                 return Math.floor(v) + '.' + z;
13805             }
13806             
13807             // now just padd decimals..
13808             var ps = String(v).split('.');
13809             var fd = (ps[1] + z);
13810             var r = fd.substring(0,n); 
13811             var rm = fd.substring(n); 
13812             if (rm < 5) {
13813                 return ps[0] + '.' + r;
13814             }
13815             r*=1; // turn it into a number;
13816             r++;
13817             if (String(r).length != n) {
13818                 ps[0]*=1;
13819                 ps[0]++;
13820                 r = String(r).substring(1); // chop the end off.
13821             }
13822             
13823             return ps[0] + '.' + r;
13824              
13825         },
13826         
13827         /**
13828          * Format a number as US currency
13829          * @param {Number/String} value The numeric value to format
13830          * @return {String} The formatted currency string
13831          */
13832         usMoney : function(v){
13833             return '$' + Roo.util.Format.number(v);
13834         },
13835         
13836         /**
13837          * Format a number
13838          * eventually this should probably emulate php's number_format
13839          * @param {Number/String} value The numeric value to format
13840          * @param {Number} decimals number of decimal places
13841          * @param {String} delimiter for thousands (default comma)
13842          * @return {String} The formatted currency string
13843          */
13844         number : function(v, decimals, thousandsDelimiter)
13845         {
13846             // multiply and round.
13847             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13848             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13849             
13850             var mul = Math.pow(10, decimals);
13851             var zero = String(mul).substring(1);
13852             v = (Math.round((v-0)*mul))/mul;
13853             
13854             // if it's '0' number.. then
13855             
13856             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13857             v = String(v);
13858             var ps = v.split('.');
13859             var whole = ps[0];
13860             
13861             var r = /(\d+)(\d{3})/;
13862             // add comma's
13863             
13864             if(thousandsDelimiter.length != 0) {
13865                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13866             } 
13867             
13868             var sub = ps[1] ?
13869                     // has decimals..
13870                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13871                     // does not have decimals
13872                     (decimals ? ('.' + zero) : '');
13873             
13874             
13875             return whole + sub ;
13876         },
13877         
13878         /**
13879          * Parse a value into a formatted date using the specified format pattern.
13880          * @param {Mixed} value The value to format
13881          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13882          * @return {String} The formatted date string
13883          */
13884         date : function(v, format){
13885             if(!v){
13886                 return "";
13887             }
13888             if(!(v instanceof Date)){
13889                 v = new Date(Date.parse(v));
13890             }
13891             return v.dateFormat(format || Roo.util.Format.defaults.date);
13892         },
13893
13894         /**
13895          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13896          * @param {String} format Any valid date format string
13897          * @return {Function} The date formatting function
13898          */
13899         dateRenderer : function(format){
13900             return function(v){
13901                 return Roo.util.Format.date(v, format);  
13902             };
13903         },
13904
13905         // private
13906         stripTagsRE : /<\/?[^>]+>/gi,
13907         
13908         /**
13909          * Strips all HTML tags
13910          * @param {Mixed} value The text from which to strip tags
13911          * @return {String} The stripped text
13912          */
13913         stripTags : function(v){
13914             return !v ? v : String(v).replace(this.stripTagsRE, "");
13915         }
13916     };
13917 }();
13918 Roo.util.Format.defaults = {
13919     date : 'd/M/Y'
13920 };/*
13921  * Based on:
13922  * Ext JS Library 1.1.1
13923  * Copyright(c) 2006-2007, Ext JS, LLC.
13924  *
13925  * Originally Released Under LGPL - original licence link has changed is not relivant.
13926  *
13927  * Fork - LGPL
13928  * <script type="text/javascript">
13929  */
13930
13931
13932  
13933
13934 /**
13935  * @class Roo.MasterTemplate
13936  * @extends Roo.Template
13937  * Provides a template that can have child templates. The syntax is:
13938 <pre><code>
13939 var t = new Roo.MasterTemplate(
13940         '&lt;select name="{name}"&gt;',
13941                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13942         '&lt;/select&gt;'
13943 );
13944 t.add('options', {value: 'foo', text: 'bar'});
13945 // or you can add multiple child elements in one shot
13946 t.addAll('options', [
13947     {value: 'foo', text: 'bar'},
13948     {value: 'foo2', text: 'bar2'},
13949     {value: 'foo3', text: 'bar3'}
13950 ]);
13951 // then append, applying the master template values
13952 t.append('my-form', {name: 'my-select'});
13953 </code></pre>
13954 * A name attribute for the child template is not required if you have only one child
13955 * template or you want to refer to them by index.
13956  */
13957 Roo.MasterTemplate = function(){
13958     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13959     this.originalHtml = this.html;
13960     var st = {};
13961     var m, re = this.subTemplateRe;
13962     re.lastIndex = 0;
13963     var subIndex = 0;
13964     while(m = re.exec(this.html)){
13965         var name = m[1], content = m[2];
13966         st[subIndex] = {
13967             name: name,
13968             index: subIndex,
13969             buffer: [],
13970             tpl : new Roo.Template(content)
13971         };
13972         if(name){
13973             st[name] = st[subIndex];
13974         }
13975         st[subIndex].tpl.compile();
13976         st[subIndex].tpl.call = this.call.createDelegate(this);
13977         subIndex++;
13978     }
13979     this.subCount = subIndex;
13980     this.subs = st;
13981 };
13982 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13983     /**
13984     * The regular expression used to match sub templates
13985     * @type RegExp
13986     * @property
13987     */
13988     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13989
13990     /**
13991      * Applies the passed values to a child template.
13992      * @param {String/Number} name (optional) The name or index of the child template
13993      * @param {Array/Object} values The values to be applied to the template
13994      * @return {MasterTemplate} this
13995      */
13996      add : function(name, values){
13997         if(arguments.length == 1){
13998             values = arguments[0];
13999             name = 0;
14000         }
14001         var s = this.subs[name];
14002         s.buffer[s.buffer.length] = s.tpl.apply(values);
14003         return this;
14004     },
14005
14006     /**
14007      * Applies all the passed values to a child template.
14008      * @param {String/Number} name (optional) The name or index of the child template
14009      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14010      * @param {Boolean} reset (optional) True to reset the template first
14011      * @return {MasterTemplate} this
14012      */
14013     fill : function(name, values, reset){
14014         var a = arguments;
14015         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14016             values = a[0];
14017             name = 0;
14018             reset = a[1];
14019         }
14020         if(reset){
14021             this.reset();
14022         }
14023         for(var i = 0, len = values.length; i < len; i++){
14024             this.add(name, values[i]);
14025         }
14026         return this;
14027     },
14028
14029     /**
14030      * Resets the template for reuse
14031      * @return {MasterTemplate} this
14032      */
14033      reset : function(){
14034         var s = this.subs;
14035         for(var i = 0; i < this.subCount; i++){
14036             s[i].buffer = [];
14037         }
14038         return this;
14039     },
14040
14041     applyTemplate : function(values){
14042         var s = this.subs;
14043         var replaceIndex = -1;
14044         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14045             return s[++replaceIndex].buffer.join("");
14046         });
14047         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14048     },
14049
14050     apply : function(){
14051         return this.applyTemplate.apply(this, arguments);
14052     },
14053
14054     compile : function(){return this;}
14055 });
14056
14057 /**
14058  * Alias for fill().
14059  * @method
14060  */
14061 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14062  /**
14063  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14064  * var tpl = Roo.MasterTemplate.from('element-id');
14065  * @param {String/HTMLElement} el
14066  * @param {Object} config
14067  * @static
14068  */
14069 Roo.MasterTemplate.from = function(el, config){
14070     el = Roo.getDom(el);
14071     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14072 };/*
14073  * Based on:
14074  * Ext JS Library 1.1.1
14075  * Copyright(c) 2006-2007, Ext JS, LLC.
14076  *
14077  * Originally Released Under LGPL - original licence link has changed is not relivant.
14078  *
14079  * Fork - LGPL
14080  * <script type="text/javascript">
14081  */
14082
14083  
14084 /**
14085  * @class Roo.util.CSS
14086  * Utility class for manipulating CSS rules
14087  * @singleton
14088  */
14089 Roo.util.CSS = function(){
14090         var rules = null;
14091         var doc = document;
14092
14093     var camelRe = /(-[a-z])/gi;
14094     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14095
14096    return {
14097    /**
14098     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14099     * tag and appended to the HEAD of the document.
14100     * @param {String|Object} cssText The text containing the css rules
14101     * @param {String} id An id to add to the stylesheet for later removal
14102     * @return {StyleSheet}
14103     */
14104     createStyleSheet : function(cssText, id){
14105         var ss;
14106         var head = doc.getElementsByTagName("head")[0];
14107         var nrules = doc.createElement("style");
14108         nrules.setAttribute("type", "text/css");
14109         if(id){
14110             nrules.setAttribute("id", id);
14111         }
14112         if (typeof(cssText) != 'string') {
14113             // support object maps..
14114             // not sure if this a good idea.. 
14115             // perhaps it should be merged with the general css handling
14116             // and handle js style props.
14117             var cssTextNew = [];
14118             for(var n in cssText) {
14119                 var citems = [];
14120                 for(var k in cssText[n]) {
14121                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14122                 }
14123                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14124                 
14125             }
14126             cssText = cssTextNew.join("\n");
14127             
14128         }
14129        
14130        
14131        if(Roo.isIE){
14132            head.appendChild(nrules);
14133            ss = nrules.styleSheet;
14134            ss.cssText = cssText;
14135        }else{
14136            try{
14137                 nrules.appendChild(doc.createTextNode(cssText));
14138            }catch(e){
14139                nrules.cssText = cssText; 
14140            }
14141            head.appendChild(nrules);
14142            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14143        }
14144        this.cacheStyleSheet(ss);
14145        return ss;
14146    },
14147
14148    /**
14149     * Removes a style or link tag by id
14150     * @param {String} id The id of the tag
14151     */
14152    removeStyleSheet : function(id){
14153        var existing = doc.getElementById(id);
14154        if(existing){
14155            existing.parentNode.removeChild(existing);
14156        }
14157    },
14158
14159    /**
14160     * Dynamically swaps an existing stylesheet reference for a new one
14161     * @param {String} id The id of an existing link tag to remove
14162     * @param {String} url The href of the new stylesheet to include
14163     */
14164    swapStyleSheet : function(id, url){
14165        this.removeStyleSheet(id);
14166        var ss = doc.createElement("link");
14167        ss.setAttribute("rel", "stylesheet");
14168        ss.setAttribute("type", "text/css");
14169        ss.setAttribute("id", id);
14170        ss.setAttribute("href", url);
14171        doc.getElementsByTagName("head")[0].appendChild(ss);
14172    },
14173    
14174    /**
14175     * Refresh the rule cache if you have dynamically added stylesheets
14176     * @return {Object} An object (hash) of rules indexed by selector
14177     */
14178    refreshCache : function(){
14179        return this.getRules(true);
14180    },
14181
14182    // private
14183    cacheStyleSheet : function(stylesheet){
14184        if(!rules){
14185            rules = {};
14186        }
14187        try{// try catch for cross domain access issue
14188            var ssRules = stylesheet.cssRules || stylesheet.rules;
14189            for(var j = ssRules.length-1; j >= 0; --j){
14190                rules[ssRules[j].selectorText] = ssRules[j];
14191            }
14192        }catch(e){}
14193    },
14194    
14195    /**
14196     * Gets all css rules for the document
14197     * @param {Boolean} refreshCache true to refresh the internal cache
14198     * @return {Object} An object (hash) of rules indexed by selector
14199     */
14200    getRules : function(refreshCache){
14201                 if(rules == null || refreshCache){
14202                         rules = {};
14203                         var ds = doc.styleSheets;
14204                         for(var i =0, len = ds.length; i < len; i++){
14205                             try{
14206                         this.cacheStyleSheet(ds[i]);
14207                     }catch(e){} 
14208                 }
14209                 }
14210                 return rules;
14211         },
14212         
14213         /**
14214     * Gets an an individual CSS rule by selector(s)
14215     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14216     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14217     * @return {CSSRule} The CSS rule or null if one is not found
14218     */
14219    getRule : function(selector, refreshCache){
14220                 var rs = this.getRules(refreshCache);
14221                 if(!(selector instanceof Array)){
14222                     return rs[selector];
14223                 }
14224                 for(var i = 0; i < selector.length; i++){
14225                         if(rs[selector[i]]){
14226                                 return rs[selector[i]];
14227                         }
14228                 }
14229                 return null;
14230         },
14231         
14232         
14233         /**
14234     * Updates a rule property
14235     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14236     * @param {String} property The css property
14237     * @param {String} value The new value for the property
14238     * @return {Boolean} true If a rule was found and updated
14239     */
14240    updateRule : function(selector, property, value){
14241                 if(!(selector instanceof Array)){
14242                         var rule = this.getRule(selector);
14243                         if(rule){
14244                                 rule.style[property.replace(camelRe, camelFn)] = value;
14245                                 return true;
14246                         }
14247                 }else{
14248                         for(var i = 0; i < selector.length; i++){
14249                                 if(this.updateRule(selector[i], property, value)){
14250                                         return true;
14251                                 }
14252                         }
14253                 }
14254                 return false;
14255         }
14256    };   
14257 }();/*
14258  * Based on:
14259  * Ext JS Library 1.1.1
14260  * Copyright(c) 2006-2007, Ext JS, LLC.
14261  *
14262  * Originally Released Under LGPL - original licence link has changed is not relivant.
14263  *
14264  * Fork - LGPL
14265  * <script type="text/javascript">
14266  */
14267
14268  
14269
14270 /**
14271  * @class Roo.util.ClickRepeater
14272  * @extends Roo.util.Observable
14273  * 
14274  * A wrapper class which can be applied to any element. Fires a "click" event while the
14275  * mouse is pressed. The interval between firings may be specified in the config but
14276  * defaults to 10 milliseconds.
14277  * 
14278  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14279  * 
14280  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14281  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14282  * Similar to an autorepeat key delay.
14283  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14284  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14285  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14286  *           "interval" and "delay" are ignored. "immediate" is honored.
14287  * @cfg {Boolean} preventDefault True to prevent the default click event
14288  * @cfg {Boolean} stopDefault True to stop the default click event
14289  * 
14290  * @history
14291  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14292  *     2007-02-02 jvs Renamed to ClickRepeater
14293  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14294  *
14295  *  @constructor
14296  * @param {String/HTMLElement/Element} el The element to listen on
14297  * @param {Object} config
14298  **/
14299 Roo.util.ClickRepeater = function(el, config)
14300 {
14301     this.el = Roo.get(el);
14302     this.el.unselectable();
14303
14304     Roo.apply(this, config);
14305
14306     this.addEvents({
14307     /**
14308      * @event mousedown
14309      * Fires when the mouse button is depressed.
14310      * @param {Roo.util.ClickRepeater} this
14311      */
14312         "mousedown" : true,
14313     /**
14314      * @event click
14315      * Fires on a specified interval during the time the element is pressed.
14316      * @param {Roo.util.ClickRepeater} this
14317      */
14318         "click" : true,
14319     /**
14320      * @event mouseup
14321      * Fires when the mouse key is released.
14322      * @param {Roo.util.ClickRepeater} this
14323      */
14324         "mouseup" : true
14325     });
14326
14327     this.el.on("mousedown", this.handleMouseDown, this);
14328     if(this.preventDefault || this.stopDefault){
14329         this.el.on("click", function(e){
14330             if(this.preventDefault){
14331                 e.preventDefault();
14332             }
14333             if(this.stopDefault){
14334                 e.stopEvent();
14335             }
14336         }, this);
14337     }
14338
14339     // allow inline handler
14340     if(this.handler){
14341         this.on("click", this.handler,  this.scope || this);
14342     }
14343
14344     Roo.util.ClickRepeater.superclass.constructor.call(this);
14345 };
14346
14347 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14348     interval : 20,
14349     delay: 250,
14350     preventDefault : true,
14351     stopDefault : false,
14352     timer : 0,
14353
14354     // private
14355     handleMouseDown : function(){
14356         clearTimeout(this.timer);
14357         this.el.blur();
14358         if(this.pressClass){
14359             this.el.addClass(this.pressClass);
14360         }
14361         this.mousedownTime = new Date();
14362
14363         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14364         this.el.on("mouseout", this.handleMouseOut, this);
14365
14366         this.fireEvent("mousedown", this);
14367         this.fireEvent("click", this);
14368         
14369         this.timer = this.click.defer(this.delay || this.interval, this);
14370     },
14371
14372     // private
14373     click : function(){
14374         this.fireEvent("click", this);
14375         this.timer = this.click.defer(this.getInterval(), this);
14376     },
14377
14378     // private
14379     getInterval: function(){
14380         if(!this.accelerate){
14381             return this.interval;
14382         }
14383         var pressTime = this.mousedownTime.getElapsed();
14384         if(pressTime < 500){
14385             return 400;
14386         }else if(pressTime < 1700){
14387             return 320;
14388         }else if(pressTime < 2600){
14389             return 250;
14390         }else if(pressTime < 3500){
14391             return 180;
14392         }else if(pressTime < 4400){
14393             return 140;
14394         }else if(pressTime < 5300){
14395             return 80;
14396         }else if(pressTime < 6200){
14397             return 50;
14398         }else{
14399             return 10;
14400         }
14401     },
14402
14403     // private
14404     handleMouseOut : function(){
14405         clearTimeout(this.timer);
14406         if(this.pressClass){
14407             this.el.removeClass(this.pressClass);
14408         }
14409         this.el.on("mouseover", this.handleMouseReturn, this);
14410     },
14411
14412     // private
14413     handleMouseReturn : function(){
14414         this.el.un("mouseover", this.handleMouseReturn);
14415         if(this.pressClass){
14416             this.el.addClass(this.pressClass);
14417         }
14418         this.click();
14419     },
14420
14421     // private
14422     handleMouseUp : function(){
14423         clearTimeout(this.timer);
14424         this.el.un("mouseover", this.handleMouseReturn);
14425         this.el.un("mouseout", this.handleMouseOut);
14426         Roo.get(document).un("mouseup", this.handleMouseUp);
14427         this.el.removeClass(this.pressClass);
14428         this.fireEvent("mouseup", this);
14429     }
14430 });/*
14431  * Based on:
14432  * Ext JS Library 1.1.1
14433  * Copyright(c) 2006-2007, Ext JS, LLC.
14434  *
14435  * Originally Released Under LGPL - original licence link has changed is not relivant.
14436  *
14437  * Fork - LGPL
14438  * <script type="text/javascript">
14439  */
14440
14441  
14442 /**
14443  * @class Roo.KeyNav
14444  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14445  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14446  * way to implement custom navigation schemes for any UI component.</p>
14447  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14448  * pageUp, pageDown, del, home, end.  Usage:</p>
14449  <pre><code>
14450 var nav = new Roo.KeyNav("my-element", {
14451     "left" : function(e){
14452         this.moveLeft(e.ctrlKey);
14453     },
14454     "right" : function(e){
14455         this.moveRight(e.ctrlKey);
14456     },
14457     "enter" : function(e){
14458         this.save();
14459     },
14460     scope : this
14461 });
14462 </code></pre>
14463  * @constructor
14464  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14465  * @param {Object} config The config
14466  */
14467 Roo.KeyNav = function(el, config){
14468     this.el = Roo.get(el);
14469     Roo.apply(this, config);
14470     if(!this.disabled){
14471         this.disabled = true;
14472         this.enable();
14473     }
14474 };
14475
14476 Roo.KeyNav.prototype = {
14477     /**
14478      * @cfg {Boolean} disabled
14479      * True to disable this KeyNav instance (defaults to false)
14480      */
14481     disabled : false,
14482     /**
14483      * @cfg {String} defaultEventAction
14484      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14485      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14486      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14487      */
14488     defaultEventAction: "stopEvent",
14489     /**
14490      * @cfg {Boolean} forceKeyDown
14491      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14492      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14493      * handle keydown instead of keypress.
14494      */
14495     forceKeyDown : false,
14496
14497     // private
14498     prepareEvent : function(e){
14499         var k = e.getKey();
14500         var h = this.keyToHandler[k];
14501         //if(h && this[h]){
14502         //    e.stopPropagation();
14503         //}
14504         if(Roo.isSafari && h && k >= 37 && k <= 40){
14505             e.stopEvent();
14506         }
14507     },
14508
14509     // private
14510     relay : function(e){
14511         var k = e.getKey();
14512         var h = this.keyToHandler[k];
14513         if(h && this[h]){
14514             if(this.doRelay(e, this[h], h) !== true){
14515                 e[this.defaultEventAction]();
14516             }
14517         }
14518     },
14519
14520     // private
14521     doRelay : function(e, h, hname){
14522         return h.call(this.scope || this, e);
14523     },
14524
14525     // possible handlers
14526     enter : false,
14527     left : false,
14528     right : false,
14529     up : false,
14530     down : false,
14531     tab : false,
14532     esc : false,
14533     pageUp : false,
14534     pageDown : false,
14535     del : false,
14536     home : false,
14537     end : false,
14538
14539     // quick lookup hash
14540     keyToHandler : {
14541         37 : "left",
14542         39 : "right",
14543         38 : "up",
14544         40 : "down",
14545         33 : "pageUp",
14546         34 : "pageDown",
14547         46 : "del",
14548         36 : "home",
14549         35 : "end",
14550         13 : "enter",
14551         27 : "esc",
14552         9  : "tab"
14553     },
14554
14555         /**
14556          * Enable this KeyNav
14557          */
14558         enable: function(){
14559                 if(this.disabled){
14560             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14561             // the EventObject will normalize Safari automatically
14562             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14563                 this.el.on("keydown", this.relay,  this);
14564             }else{
14565                 this.el.on("keydown", this.prepareEvent,  this);
14566                 this.el.on("keypress", this.relay,  this);
14567             }
14568                     this.disabled = false;
14569                 }
14570         },
14571
14572         /**
14573          * Disable this KeyNav
14574          */
14575         disable: function(){
14576                 if(!this.disabled){
14577                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14578                 this.el.un("keydown", this.relay);
14579             }else{
14580                 this.el.un("keydown", this.prepareEvent);
14581                 this.el.un("keypress", this.relay);
14582             }
14583                     this.disabled = true;
14584                 }
14585         }
14586 };/*
14587  * Based on:
14588  * Ext JS Library 1.1.1
14589  * Copyright(c) 2006-2007, Ext JS, LLC.
14590  *
14591  * Originally Released Under LGPL - original licence link has changed is not relivant.
14592  *
14593  * Fork - LGPL
14594  * <script type="text/javascript">
14595  */
14596
14597  
14598 /**
14599  * @class Roo.KeyMap
14600  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14601  * The constructor accepts the same config object as defined by {@link #addBinding}.
14602  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14603  * combination it will call the function with this signature (if the match is a multi-key
14604  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14605  * A KeyMap can also handle a string representation of keys.<br />
14606  * Usage:
14607  <pre><code>
14608 // map one key by key code
14609 var map = new Roo.KeyMap("my-element", {
14610     key: 13, // or Roo.EventObject.ENTER
14611     fn: myHandler,
14612     scope: myObject
14613 });
14614
14615 // map multiple keys to one action by string
14616 var map = new Roo.KeyMap("my-element", {
14617     key: "a\r\n\t",
14618     fn: myHandler,
14619     scope: myObject
14620 });
14621
14622 // map multiple keys to multiple actions by strings and array of codes
14623 var map = new Roo.KeyMap("my-element", [
14624     {
14625         key: [10,13],
14626         fn: function(){ alert("Return was pressed"); }
14627     }, {
14628         key: "abc",
14629         fn: function(){ alert('a, b or c was pressed'); }
14630     }, {
14631         key: "\t",
14632         ctrl:true,
14633         shift:true,
14634         fn: function(){ alert('Control + shift + tab was pressed.'); }
14635     }
14636 ]);
14637 </code></pre>
14638  * <b>Note: A KeyMap starts enabled</b>
14639  * @constructor
14640  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14641  * @param {Object} config The config (see {@link #addBinding})
14642  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14643  */
14644 Roo.KeyMap = function(el, config, eventName){
14645     this.el  = Roo.get(el);
14646     this.eventName = eventName || "keydown";
14647     this.bindings = [];
14648     if(config){
14649         this.addBinding(config);
14650     }
14651     this.enable();
14652 };
14653
14654 Roo.KeyMap.prototype = {
14655     /**
14656      * True to stop the event from bubbling and prevent the default browser action if the
14657      * key was handled by the KeyMap (defaults to false)
14658      * @type Boolean
14659      */
14660     stopEvent : false,
14661
14662     /**
14663      * Add a new binding to this KeyMap. The following config object properties are supported:
14664      * <pre>
14665 Property    Type             Description
14666 ----------  ---------------  ----------------------------------------------------------------------
14667 key         String/Array     A single keycode or an array of keycodes to handle
14668 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14669 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14670 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14671 fn          Function         The function to call when KeyMap finds the expected key combination
14672 scope       Object           The scope of the callback function
14673 </pre>
14674      *
14675      * Usage:
14676      * <pre><code>
14677 // Create a KeyMap
14678 var map = new Roo.KeyMap(document, {
14679     key: Roo.EventObject.ENTER,
14680     fn: handleKey,
14681     scope: this
14682 });
14683
14684 //Add a new binding to the existing KeyMap later
14685 map.addBinding({
14686     key: 'abc',
14687     shift: true,
14688     fn: handleKey,
14689     scope: this
14690 });
14691 </code></pre>
14692      * @param {Object/Array} config A single KeyMap config or an array of configs
14693      */
14694         addBinding : function(config){
14695         if(config instanceof Array){
14696             for(var i = 0, len = config.length; i < len; i++){
14697                 this.addBinding(config[i]);
14698             }
14699             return;
14700         }
14701         var keyCode = config.key,
14702             shift = config.shift, 
14703             ctrl = config.ctrl, 
14704             alt = config.alt,
14705             fn = config.fn,
14706             scope = config.scope;
14707         if(typeof keyCode == "string"){
14708             var ks = [];
14709             var keyString = keyCode.toUpperCase();
14710             for(var j = 0, len = keyString.length; j < len; j++){
14711                 ks.push(keyString.charCodeAt(j));
14712             }
14713             keyCode = ks;
14714         }
14715         var keyArray = keyCode instanceof Array;
14716         var handler = function(e){
14717             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14718                 var k = e.getKey();
14719                 if(keyArray){
14720                     for(var i = 0, len = keyCode.length; i < len; i++){
14721                         if(keyCode[i] == k){
14722                           if(this.stopEvent){
14723                               e.stopEvent();
14724                           }
14725                           fn.call(scope || window, k, e);
14726                           return;
14727                         }
14728                     }
14729                 }else{
14730                     if(k == keyCode){
14731                         if(this.stopEvent){
14732                            e.stopEvent();
14733                         }
14734                         fn.call(scope || window, k, e);
14735                     }
14736                 }
14737             }
14738         };
14739         this.bindings.push(handler);  
14740         },
14741
14742     /**
14743      * Shorthand for adding a single key listener
14744      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14745      * following options:
14746      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14747      * @param {Function} fn The function to call
14748      * @param {Object} scope (optional) The scope of the function
14749      */
14750     on : function(key, fn, scope){
14751         var keyCode, shift, ctrl, alt;
14752         if(typeof key == "object" && !(key instanceof Array)){
14753             keyCode = key.key;
14754             shift = key.shift;
14755             ctrl = key.ctrl;
14756             alt = key.alt;
14757         }else{
14758             keyCode = key;
14759         }
14760         this.addBinding({
14761             key: keyCode,
14762             shift: shift,
14763             ctrl: ctrl,
14764             alt: alt,
14765             fn: fn,
14766             scope: scope
14767         })
14768     },
14769
14770     // private
14771     handleKeyDown : function(e){
14772             if(this.enabled){ //just in case
14773             var b = this.bindings;
14774             for(var i = 0, len = b.length; i < len; i++){
14775                 b[i].call(this, e);
14776             }
14777             }
14778         },
14779         
14780         /**
14781          * Returns true if this KeyMap is enabled
14782          * @return {Boolean} 
14783          */
14784         isEnabled : function(){
14785             return this.enabled;  
14786         },
14787         
14788         /**
14789          * Enables this KeyMap
14790          */
14791         enable: function(){
14792                 if(!this.enabled){
14793                     this.el.on(this.eventName, this.handleKeyDown, this);
14794                     this.enabled = true;
14795                 }
14796         },
14797
14798         /**
14799          * Disable this KeyMap
14800          */
14801         disable: function(){
14802                 if(this.enabled){
14803                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14804                     this.enabled = false;
14805                 }
14806         }
14807 };/*
14808  * Based on:
14809  * Ext JS Library 1.1.1
14810  * Copyright(c) 2006-2007, Ext JS, LLC.
14811  *
14812  * Originally Released Under LGPL - original licence link has changed is not relivant.
14813  *
14814  * Fork - LGPL
14815  * <script type="text/javascript">
14816  */
14817
14818  
14819 /**
14820  * @class Roo.util.TextMetrics
14821  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14822  * wide, in pixels, a given block of text will be.
14823  * @singleton
14824  */
14825 Roo.util.TextMetrics = function(){
14826     var shared;
14827     return {
14828         /**
14829          * Measures the size of the specified text
14830          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14831          * that can affect the size of the rendered text
14832          * @param {String} text The text to measure
14833          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14834          * in order to accurately measure the text height
14835          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14836          */
14837         measure : function(el, text, fixedWidth){
14838             if(!shared){
14839                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14840             }
14841             shared.bind(el);
14842             shared.setFixedWidth(fixedWidth || 'auto');
14843             return shared.getSize(text);
14844         },
14845
14846         /**
14847          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14848          * the overhead of multiple calls to initialize the style properties on each measurement.
14849          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14850          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14851          * in order to accurately measure the text height
14852          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14853          */
14854         createInstance : function(el, fixedWidth){
14855             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14856         }
14857     };
14858 }();
14859
14860  
14861
14862 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14863     var ml = new Roo.Element(document.createElement('div'));
14864     document.body.appendChild(ml.dom);
14865     ml.position('absolute');
14866     ml.setLeftTop(-1000, -1000);
14867     ml.hide();
14868
14869     if(fixedWidth){
14870         ml.setWidth(fixedWidth);
14871     }
14872      
14873     var instance = {
14874         /**
14875          * Returns the size of the specified text based on the internal element's style and width properties
14876          * @memberOf Roo.util.TextMetrics.Instance#
14877          * @param {String} text The text to measure
14878          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14879          */
14880         getSize : function(text){
14881             ml.update(text);
14882             var s = ml.getSize();
14883             ml.update('');
14884             return s;
14885         },
14886
14887         /**
14888          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14889          * that can affect the size of the rendered text
14890          * @memberOf Roo.util.TextMetrics.Instance#
14891          * @param {String/HTMLElement} el The element, dom node or id
14892          */
14893         bind : function(el){
14894             ml.setStyle(
14895                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14896             );
14897         },
14898
14899         /**
14900          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14901          * to set a fixed width in order to accurately measure the text height.
14902          * @memberOf Roo.util.TextMetrics.Instance#
14903          * @param {Number} width The width to set on the element
14904          */
14905         setFixedWidth : function(width){
14906             ml.setWidth(width);
14907         },
14908
14909         /**
14910          * Returns the measured width of the specified text
14911          * @memberOf Roo.util.TextMetrics.Instance#
14912          * @param {String} text The text to measure
14913          * @return {Number} width The width in pixels
14914          */
14915         getWidth : function(text){
14916             ml.dom.style.width = 'auto';
14917             return this.getSize(text).width;
14918         },
14919
14920         /**
14921          * Returns the measured height of the specified text.  For multiline text, be sure to call
14922          * {@link #setFixedWidth} if necessary.
14923          * @memberOf Roo.util.TextMetrics.Instance#
14924          * @param {String} text The text to measure
14925          * @return {Number} height The height in pixels
14926          */
14927         getHeight : function(text){
14928             return this.getSize(text).height;
14929         }
14930     };
14931
14932     instance.bind(bindTo);
14933
14934     return instance;
14935 };
14936
14937 // backwards compat
14938 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14939  * Based on:
14940  * Ext JS Library 1.1.1
14941  * Copyright(c) 2006-2007, Ext JS, LLC.
14942  *
14943  * Originally Released Under LGPL - original licence link has changed is not relivant.
14944  *
14945  * Fork - LGPL
14946  * <script type="text/javascript">
14947  */
14948
14949 /**
14950  * @class Roo.state.Provider
14951  * Abstract base class for state provider implementations. This class provides methods
14952  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14953  * Provider interface.
14954  */
14955 Roo.state.Provider = function(){
14956     /**
14957      * @event statechange
14958      * Fires when a state change occurs.
14959      * @param {Provider} this This state provider
14960      * @param {String} key The state key which was changed
14961      * @param {String} value The encoded value for the state
14962      */
14963     this.addEvents({
14964         "statechange": true
14965     });
14966     this.state = {};
14967     Roo.state.Provider.superclass.constructor.call(this);
14968 };
14969 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14970     /**
14971      * Returns the current value for a key
14972      * @param {String} name The key name
14973      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14974      * @return {Mixed} The state data
14975      */
14976     get : function(name, defaultValue){
14977         return typeof this.state[name] == "undefined" ?
14978             defaultValue : this.state[name];
14979     },
14980     
14981     /**
14982      * Clears a value from the state
14983      * @param {String} name The key name
14984      */
14985     clear : function(name){
14986         delete this.state[name];
14987         this.fireEvent("statechange", this, name, null);
14988     },
14989     
14990     /**
14991      * Sets the value for a key
14992      * @param {String} name The key name
14993      * @param {Mixed} value The value to set
14994      */
14995     set : function(name, value){
14996         this.state[name] = value;
14997         this.fireEvent("statechange", this, name, value);
14998     },
14999     
15000     /**
15001      * Decodes a string previously encoded with {@link #encodeValue}.
15002      * @param {String} value The value to decode
15003      * @return {Mixed} The decoded value
15004      */
15005     decodeValue : function(cookie){
15006         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15007         var matches = re.exec(unescape(cookie));
15008         if(!matches || !matches[1]) {
15009             return; // non state cookie
15010         }
15011         var type = matches[1];
15012         var v = matches[2];
15013         switch(type){
15014             case "n":
15015                 return parseFloat(v);
15016             case "d":
15017                 return new Date(Date.parse(v));
15018             case "b":
15019                 return (v == "1");
15020             case "a":
15021                 var all = [];
15022                 var values = v.split("^");
15023                 for(var i = 0, len = values.length; i < len; i++){
15024                     all.push(this.decodeValue(values[i]));
15025                 }
15026                 return all;
15027            case "o":
15028                 var all = {};
15029                 var values = v.split("^");
15030                 for(var i = 0, len = values.length; i < len; i++){
15031                     var kv = values[i].split("=");
15032                     all[kv[0]] = this.decodeValue(kv[1]);
15033                 }
15034                 return all;
15035            default:
15036                 return v;
15037         }
15038     },
15039     
15040     /**
15041      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15042      * @param {Mixed} value The value to encode
15043      * @return {String} The encoded value
15044      */
15045     encodeValue : function(v){
15046         var enc;
15047         if(typeof v == "number"){
15048             enc = "n:" + v;
15049         }else if(typeof v == "boolean"){
15050             enc = "b:" + (v ? "1" : "0");
15051         }else if(v instanceof Date){
15052             enc = "d:" + v.toGMTString();
15053         }else if(v instanceof Array){
15054             var flat = "";
15055             for(var i = 0, len = v.length; i < len; i++){
15056                 flat += this.encodeValue(v[i]);
15057                 if(i != len-1) {
15058                     flat += "^";
15059                 }
15060             }
15061             enc = "a:" + flat;
15062         }else if(typeof v == "object"){
15063             var flat = "";
15064             for(var key in v){
15065                 if(typeof v[key] != "function"){
15066                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15067                 }
15068             }
15069             enc = "o:" + flat.substring(0, flat.length-1);
15070         }else{
15071             enc = "s:" + v;
15072         }
15073         return escape(enc);        
15074     }
15075 });
15076
15077 /*
15078  * Based on:
15079  * Ext JS Library 1.1.1
15080  * Copyright(c) 2006-2007, Ext JS, LLC.
15081  *
15082  * Originally Released Under LGPL - original licence link has changed is not relivant.
15083  *
15084  * Fork - LGPL
15085  * <script type="text/javascript">
15086  */
15087 /**
15088  * @class Roo.state.Manager
15089  * This is the global state manager. By default all components that are "state aware" check this class
15090  * for state information if you don't pass them a custom state provider. In order for this class
15091  * to be useful, it must be initialized with a provider when your application initializes.
15092  <pre><code>
15093 // in your initialization function
15094 init : function(){
15095    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15096    ...
15097    // supposed you have a {@link Roo.BorderLayout}
15098    var layout = new Roo.BorderLayout(...);
15099    layout.restoreState();
15100    // or a {Roo.BasicDialog}
15101    var dialog = new Roo.BasicDialog(...);
15102    dialog.restoreState();
15103  </code></pre>
15104  * @singleton
15105  */
15106 Roo.state.Manager = function(){
15107     var provider = new Roo.state.Provider();
15108     
15109     return {
15110         /**
15111          * Configures the default state provider for your application
15112          * @param {Provider} stateProvider The state provider to set
15113          */
15114         setProvider : function(stateProvider){
15115             provider = stateProvider;
15116         },
15117         
15118         /**
15119          * Returns the current value for a key
15120          * @param {String} name The key name
15121          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15122          * @return {Mixed} The state data
15123          */
15124         get : function(key, defaultValue){
15125             return provider.get(key, defaultValue);
15126         },
15127         
15128         /**
15129          * Sets the value for a key
15130          * @param {String} name The key name
15131          * @param {Mixed} value The state data
15132          */
15133          set : function(key, value){
15134             provider.set(key, value);
15135         },
15136         
15137         /**
15138          * Clears a value from the state
15139          * @param {String} name The key name
15140          */
15141         clear : function(key){
15142             provider.clear(key);
15143         },
15144         
15145         /**
15146          * Gets the currently configured state provider
15147          * @return {Provider} The state provider
15148          */
15149         getProvider : function(){
15150             return provider;
15151         }
15152     };
15153 }();
15154 /*
15155  * Based on:
15156  * Ext JS Library 1.1.1
15157  * Copyright(c) 2006-2007, Ext JS, LLC.
15158  *
15159  * Originally Released Under LGPL - original licence link has changed is not relivant.
15160  *
15161  * Fork - LGPL
15162  * <script type="text/javascript">
15163  */
15164 /**
15165  * @class Roo.state.CookieProvider
15166  * @extends Roo.state.Provider
15167  * The default Provider implementation which saves state via cookies.
15168  * <br />Usage:
15169  <pre><code>
15170    var cp = new Roo.state.CookieProvider({
15171        path: "/cgi-bin/",
15172        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15173        domain: "roojs.com"
15174    })
15175    Roo.state.Manager.setProvider(cp);
15176  </code></pre>
15177  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15178  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15179  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15180  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15181  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15182  * domain the page is running on including the 'www' like 'www.roojs.com')
15183  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15184  * @constructor
15185  * Create a new CookieProvider
15186  * @param {Object} config The configuration object
15187  */
15188 Roo.state.CookieProvider = function(config){
15189     Roo.state.CookieProvider.superclass.constructor.call(this);
15190     this.path = "/";
15191     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15192     this.domain = null;
15193     this.secure = false;
15194     Roo.apply(this, config);
15195     this.state = this.readCookies();
15196 };
15197
15198 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15199     // private
15200     set : function(name, value){
15201         if(typeof value == "undefined" || value === null){
15202             this.clear(name);
15203             return;
15204         }
15205         this.setCookie(name, value);
15206         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15207     },
15208
15209     // private
15210     clear : function(name){
15211         this.clearCookie(name);
15212         Roo.state.CookieProvider.superclass.clear.call(this, name);
15213     },
15214
15215     // private
15216     readCookies : function(){
15217         var cookies = {};
15218         var c = document.cookie + ";";
15219         var re = /\s?(.*?)=(.*?);/g;
15220         var matches;
15221         while((matches = re.exec(c)) != null){
15222             var name = matches[1];
15223             var value = matches[2];
15224             if(name && name.substring(0,3) == "ys-"){
15225                 cookies[name.substr(3)] = this.decodeValue(value);
15226             }
15227         }
15228         return cookies;
15229     },
15230
15231     // private
15232     setCookie : function(name, value){
15233         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15234            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15235            ((this.path == null) ? "" : ("; path=" + this.path)) +
15236            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15237            ((this.secure == true) ? "; secure" : "");
15238     },
15239
15240     // private
15241     clearCookie : function(name){
15242         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15243            ((this.path == null) ? "" : ("; path=" + this.path)) +
15244            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15245            ((this.secure == true) ? "; secure" : "");
15246     }
15247 });/*
15248  * Based on:
15249  * Ext JS Library 1.1.1
15250  * Copyright(c) 2006-2007, Ext JS, LLC.
15251  *
15252  * Originally Released Under LGPL - original licence link has changed is not relivant.
15253  *
15254  * Fork - LGPL
15255  * <script type="text/javascript">
15256  */
15257  
15258
15259 /**
15260  * @class Roo.ComponentMgr
15261  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15262  * @singleton
15263  */
15264 Roo.ComponentMgr = function(){
15265     var all = new Roo.util.MixedCollection();
15266
15267     return {
15268         /**
15269          * Registers a component.
15270          * @param {Roo.Component} c The component
15271          */
15272         register : function(c){
15273             all.add(c);
15274         },
15275
15276         /**
15277          * Unregisters a component.
15278          * @param {Roo.Component} c The component
15279          */
15280         unregister : function(c){
15281             all.remove(c);
15282         },
15283
15284         /**
15285          * Returns a component by id
15286          * @param {String} id The component id
15287          */
15288         get : function(id){
15289             return all.get(id);
15290         },
15291
15292         /**
15293          * Registers a function that will be called when a specified component is added to ComponentMgr
15294          * @param {String} id The component id
15295          * @param {Funtction} fn The callback function
15296          * @param {Object} scope The scope of the callback
15297          */
15298         onAvailable : function(id, fn, scope){
15299             all.on("add", function(index, o){
15300                 if(o.id == id){
15301                     fn.call(scope || o, o);
15302                     all.un("add", fn, scope);
15303                 }
15304             });
15305         }
15306     };
15307 }();/*
15308  * Based on:
15309  * Ext JS Library 1.1.1
15310  * Copyright(c) 2006-2007, Ext JS, LLC.
15311  *
15312  * Originally Released Under LGPL - original licence link has changed is not relivant.
15313  *
15314  * Fork - LGPL
15315  * <script type="text/javascript">
15316  */
15317  
15318 /**
15319  * @class Roo.Component
15320  * @extends Roo.util.Observable
15321  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15322  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15323  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15324  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15325  * All visual components (widgets) that require rendering into a layout should subclass Component.
15326  * @constructor
15327  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15328  * 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
15329  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15330  */
15331 Roo.Component = function(config){
15332     config = config || {};
15333     if(config.tagName || config.dom || typeof config == "string"){ // element object
15334         config = {el: config, id: config.id || config};
15335     }
15336     this.initialConfig = config;
15337
15338     Roo.apply(this, config);
15339     this.addEvents({
15340         /**
15341          * @event disable
15342          * Fires after the component is disabled.
15343              * @param {Roo.Component} this
15344              */
15345         disable : true,
15346         /**
15347          * @event enable
15348          * Fires after the component is enabled.
15349              * @param {Roo.Component} this
15350              */
15351         enable : true,
15352         /**
15353          * @event beforeshow
15354          * Fires before the component is shown.  Return false to stop the show.
15355              * @param {Roo.Component} this
15356              */
15357         beforeshow : true,
15358         /**
15359          * @event show
15360          * Fires after the component is shown.
15361              * @param {Roo.Component} this
15362              */
15363         show : true,
15364         /**
15365          * @event beforehide
15366          * Fires before the component is hidden. Return false to stop the hide.
15367              * @param {Roo.Component} this
15368              */
15369         beforehide : true,
15370         /**
15371          * @event hide
15372          * Fires after the component is hidden.
15373              * @param {Roo.Component} this
15374              */
15375         hide : true,
15376         /**
15377          * @event beforerender
15378          * Fires before the component is rendered. Return false to stop the render.
15379              * @param {Roo.Component} this
15380              */
15381         beforerender : true,
15382         /**
15383          * @event render
15384          * Fires after the component is rendered.
15385              * @param {Roo.Component} this
15386              */
15387         render : true,
15388         /**
15389          * @event beforedestroy
15390          * Fires before the component is destroyed. Return false to stop the destroy.
15391              * @param {Roo.Component} this
15392              */
15393         beforedestroy : true,
15394         /**
15395          * @event destroy
15396          * Fires after the component is destroyed.
15397              * @param {Roo.Component} this
15398              */
15399         destroy : true
15400     });
15401     if(!this.id){
15402         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15403     }
15404     Roo.ComponentMgr.register(this);
15405     Roo.Component.superclass.constructor.call(this);
15406     this.initComponent();
15407     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15408         this.render(this.renderTo);
15409         delete this.renderTo;
15410     }
15411 };
15412
15413 /** @private */
15414 Roo.Component.AUTO_ID = 1000;
15415
15416 Roo.extend(Roo.Component, Roo.util.Observable, {
15417     /**
15418      * @scope Roo.Component.prototype
15419      * @type {Boolean}
15420      * true if this component is hidden. Read-only.
15421      */
15422     hidden : false,
15423     /**
15424      * @type {Boolean}
15425      * true if this component is disabled. Read-only.
15426      */
15427     disabled : false,
15428     /**
15429      * @type {Boolean}
15430      * true if this component has been rendered. Read-only.
15431      */
15432     rendered : false,
15433     
15434     /** @cfg {String} disableClass
15435      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15436      */
15437     disabledClass : "x-item-disabled",
15438         /** @cfg {Boolean} allowDomMove
15439          * Whether the component can move the Dom node when rendering (defaults to true).
15440          */
15441     allowDomMove : true,
15442     /** @cfg {String} hideMode (display|visibility)
15443      * How this component should hidden. Supported values are
15444      * "visibility" (css visibility), "offsets" (negative offset position) and
15445      * "display" (css display) - defaults to "display".
15446      */
15447     hideMode: 'display',
15448
15449     /** @private */
15450     ctype : "Roo.Component",
15451
15452     /**
15453      * @cfg {String} actionMode 
15454      * which property holds the element that used for  hide() / show() / disable() / enable()
15455      * default is 'el' for forms you probably want to set this to fieldEl 
15456      */
15457     actionMode : "el",
15458
15459     /** @private */
15460     getActionEl : function(){
15461         return this[this.actionMode];
15462     },
15463
15464     initComponent : Roo.emptyFn,
15465     /**
15466      * If this is a lazy rendering component, render it to its container element.
15467      * @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.
15468      */
15469     render : function(container, position){
15470         
15471         if(this.rendered){
15472             return this;
15473         }
15474         
15475         if(this.fireEvent("beforerender", this) === false){
15476             return false;
15477         }
15478         
15479         if(!container && this.el){
15480             this.el = Roo.get(this.el);
15481             container = this.el.dom.parentNode;
15482             this.allowDomMove = false;
15483         }
15484         this.container = Roo.get(container);
15485         this.rendered = true;
15486         if(position !== undefined){
15487             if(typeof position == 'number'){
15488                 position = this.container.dom.childNodes[position];
15489             }else{
15490                 position = Roo.getDom(position);
15491             }
15492         }
15493         this.onRender(this.container, position || null);
15494         if(this.cls){
15495             this.el.addClass(this.cls);
15496             delete this.cls;
15497         }
15498         if(this.style){
15499             this.el.applyStyles(this.style);
15500             delete this.style;
15501         }
15502         this.fireEvent("render", this);
15503         this.afterRender(this.container);
15504         if(this.hidden){
15505             this.hide();
15506         }
15507         if(this.disabled){
15508             this.disable();
15509         }
15510
15511         return this;
15512         
15513     },
15514
15515     /** @private */
15516     // default function is not really useful
15517     onRender : function(ct, position){
15518         if(this.el){
15519             this.el = Roo.get(this.el);
15520             if(this.allowDomMove !== false){
15521                 ct.dom.insertBefore(this.el.dom, position);
15522             }
15523         }
15524     },
15525
15526     /** @private */
15527     getAutoCreate : function(){
15528         var cfg = typeof this.autoCreate == "object" ?
15529                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15530         if(this.id && !cfg.id){
15531             cfg.id = this.id;
15532         }
15533         return cfg;
15534     },
15535
15536     /** @private */
15537     afterRender : Roo.emptyFn,
15538
15539     /**
15540      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15541      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15542      */
15543     destroy : function(){
15544         if(this.fireEvent("beforedestroy", this) !== false){
15545             this.purgeListeners();
15546             this.beforeDestroy();
15547             if(this.rendered){
15548                 this.el.removeAllListeners();
15549                 this.el.remove();
15550                 if(this.actionMode == "container"){
15551                     this.container.remove();
15552                 }
15553             }
15554             this.onDestroy();
15555             Roo.ComponentMgr.unregister(this);
15556             this.fireEvent("destroy", this);
15557         }
15558     },
15559
15560         /** @private */
15561     beforeDestroy : function(){
15562
15563     },
15564
15565         /** @private */
15566         onDestroy : function(){
15567
15568     },
15569
15570     /**
15571      * Returns the underlying {@link Roo.Element}.
15572      * @return {Roo.Element} The element
15573      */
15574     getEl : function(){
15575         return this.el;
15576     },
15577
15578     /**
15579      * Returns the id of this component.
15580      * @return {String}
15581      */
15582     getId : function(){
15583         return this.id;
15584     },
15585
15586     /**
15587      * Try to focus this component.
15588      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15589      * @return {Roo.Component} this
15590      */
15591     focus : function(selectText){
15592         if(this.rendered){
15593             this.el.focus();
15594             if(selectText === true){
15595                 this.el.dom.select();
15596             }
15597         }
15598         return this;
15599     },
15600
15601     /** @private */
15602     blur : function(){
15603         if(this.rendered){
15604             this.el.blur();
15605         }
15606         return this;
15607     },
15608
15609     /**
15610      * Disable this component.
15611      * @return {Roo.Component} this
15612      */
15613     disable : function(){
15614         if(this.rendered){
15615             this.onDisable();
15616         }
15617         this.disabled = true;
15618         this.fireEvent("disable", this);
15619         return this;
15620     },
15621
15622         // private
15623     onDisable : function(){
15624         this.getActionEl().addClass(this.disabledClass);
15625         this.el.dom.disabled = true;
15626     },
15627
15628     /**
15629      * Enable this component.
15630      * @return {Roo.Component} this
15631      */
15632     enable : function(){
15633         if(this.rendered){
15634             this.onEnable();
15635         }
15636         this.disabled = false;
15637         this.fireEvent("enable", this);
15638         return this;
15639     },
15640
15641         // private
15642     onEnable : function(){
15643         this.getActionEl().removeClass(this.disabledClass);
15644         this.el.dom.disabled = false;
15645     },
15646
15647     /**
15648      * Convenience function for setting disabled/enabled by boolean.
15649      * @param {Boolean} disabled
15650      */
15651     setDisabled : function(disabled){
15652         this[disabled ? "disable" : "enable"]();
15653     },
15654
15655     /**
15656      * Show this component.
15657      * @return {Roo.Component} this
15658      */
15659     show: function(){
15660         if(this.fireEvent("beforeshow", this) !== false){
15661             this.hidden = false;
15662             if(this.rendered){
15663                 this.onShow();
15664             }
15665             this.fireEvent("show", this);
15666         }
15667         return this;
15668     },
15669
15670     // private
15671     onShow : function(){
15672         var ae = this.getActionEl();
15673         if(this.hideMode == 'visibility'){
15674             ae.dom.style.visibility = "visible";
15675         }else if(this.hideMode == 'offsets'){
15676             ae.removeClass('x-hidden');
15677         }else{
15678             ae.dom.style.display = "";
15679         }
15680     },
15681
15682     /**
15683      * Hide this component.
15684      * @return {Roo.Component} this
15685      */
15686     hide: function(){
15687         if(this.fireEvent("beforehide", this) !== false){
15688             this.hidden = true;
15689             if(this.rendered){
15690                 this.onHide();
15691             }
15692             this.fireEvent("hide", this);
15693         }
15694         return this;
15695     },
15696
15697     // private
15698     onHide : function(){
15699         var ae = this.getActionEl();
15700         if(this.hideMode == 'visibility'){
15701             ae.dom.style.visibility = "hidden";
15702         }else if(this.hideMode == 'offsets'){
15703             ae.addClass('x-hidden');
15704         }else{
15705             ae.dom.style.display = "none";
15706         }
15707     },
15708
15709     /**
15710      * Convenience function to hide or show this component by boolean.
15711      * @param {Boolean} visible True to show, false to hide
15712      * @return {Roo.Component} this
15713      */
15714     setVisible: function(visible){
15715         if(visible) {
15716             this.show();
15717         }else{
15718             this.hide();
15719         }
15720         return this;
15721     },
15722
15723     /**
15724      * Returns true if this component is visible.
15725      */
15726     isVisible : function(){
15727         return this.getActionEl().isVisible();
15728     },
15729
15730     cloneConfig : function(overrides){
15731         overrides = overrides || {};
15732         var id = overrides.id || Roo.id();
15733         var cfg = Roo.applyIf(overrides, this.initialConfig);
15734         cfg.id = id; // prevent dup id
15735         return new this.constructor(cfg);
15736     }
15737 });/*
15738  * Based on:
15739  * Ext JS Library 1.1.1
15740  * Copyright(c) 2006-2007, Ext JS, LLC.
15741  *
15742  * Originally Released Under LGPL - original licence link has changed is not relivant.
15743  *
15744  * Fork - LGPL
15745  * <script type="text/javascript">
15746  */
15747
15748 /**
15749  * @class Roo.BoxComponent
15750  * @extends Roo.Component
15751  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15752  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15753  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15754  * layout containers.
15755  * @constructor
15756  * @param {Roo.Element/String/Object} config The configuration options.
15757  */
15758 Roo.BoxComponent = function(config){
15759     Roo.Component.call(this, config);
15760     this.addEvents({
15761         /**
15762          * @event resize
15763          * Fires after the component is resized.
15764              * @param {Roo.Component} this
15765              * @param {Number} adjWidth The box-adjusted width that was set
15766              * @param {Number} adjHeight The box-adjusted height that was set
15767              * @param {Number} rawWidth The width that was originally specified
15768              * @param {Number} rawHeight The height that was originally specified
15769              */
15770         resize : true,
15771         /**
15772          * @event move
15773          * Fires after the component is moved.
15774              * @param {Roo.Component} this
15775              * @param {Number} x The new x position
15776              * @param {Number} y The new y position
15777              */
15778         move : true
15779     });
15780 };
15781
15782 Roo.extend(Roo.BoxComponent, Roo.Component, {
15783     // private, set in afterRender to signify that the component has been rendered
15784     boxReady : false,
15785     // private, used to defer height settings to subclasses
15786     deferHeight: false,
15787     /** @cfg {Number} width
15788      * width (optional) size of component
15789      */
15790      /** @cfg {Number} height
15791      * height (optional) size of component
15792      */
15793      
15794     /**
15795      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15796      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15797      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15798      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15799      * @return {Roo.BoxComponent} this
15800      */
15801     setSize : function(w, h){
15802         // support for standard size objects
15803         if(typeof w == 'object'){
15804             h = w.height;
15805             w = w.width;
15806         }
15807         // not rendered
15808         if(!this.boxReady){
15809             this.width = w;
15810             this.height = h;
15811             return this;
15812         }
15813
15814         // prevent recalcs when not needed
15815         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15816             return this;
15817         }
15818         this.lastSize = {width: w, height: h};
15819
15820         var adj = this.adjustSize(w, h);
15821         var aw = adj.width, ah = adj.height;
15822         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15823             var rz = this.getResizeEl();
15824             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15825                 rz.setSize(aw, ah);
15826             }else if(!this.deferHeight && ah !== undefined){
15827                 rz.setHeight(ah);
15828             }else if(aw !== undefined){
15829                 rz.setWidth(aw);
15830             }
15831             this.onResize(aw, ah, w, h);
15832             this.fireEvent('resize', this, aw, ah, w, h);
15833         }
15834         return this;
15835     },
15836
15837     /**
15838      * Gets the current size of the component's underlying element.
15839      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15840      */
15841     getSize : function(){
15842         return this.el.getSize();
15843     },
15844
15845     /**
15846      * Gets the current XY position of the component's underlying element.
15847      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15848      * @return {Array} The XY position of the element (e.g., [100, 200])
15849      */
15850     getPosition : function(local){
15851         if(local === true){
15852             return [this.el.getLeft(true), this.el.getTop(true)];
15853         }
15854         return this.xy || this.el.getXY();
15855     },
15856
15857     /**
15858      * Gets the current box measurements of the component's underlying element.
15859      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15860      * @returns {Object} box An object in the format {x, y, width, height}
15861      */
15862     getBox : function(local){
15863         var s = this.el.getSize();
15864         if(local){
15865             s.x = this.el.getLeft(true);
15866             s.y = this.el.getTop(true);
15867         }else{
15868             var xy = this.xy || this.el.getXY();
15869             s.x = xy[0];
15870             s.y = xy[1];
15871         }
15872         return s;
15873     },
15874
15875     /**
15876      * Sets the current box measurements of the component's underlying element.
15877      * @param {Object} box An object in the format {x, y, width, height}
15878      * @returns {Roo.BoxComponent} this
15879      */
15880     updateBox : function(box){
15881         this.setSize(box.width, box.height);
15882         this.setPagePosition(box.x, box.y);
15883         return this;
15884     },
15885
15886     // protected
15887     getResizeEl : function(){
15888         return this.resizeEl || this.el;
15889     },
15890
15891     // protected
15892     getPositionEl : function(){
15893         return this.positionEl || this.el;
15894     },
15895
15896     /**
15897      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15898      * This method fires the move event.
15899      * @param {Number} left The new left
15900      * @param {Number} top The new top
15901      * @returns {Roo.BoxComponent} this
15902      */
15903     setPosition : function(x, y){
15904         this.x = x;
15905         this.y = y;
15906         if(!this.boxReady){
15907             return this;
15908         }
15909         var adj = this.adjustPosition(x, y);
15910         var ax = adj.x, ay = adj.y;
15911
15912         var el = this.getPositionEl();
15913         if(ax !== undefined || ay !== undefined){
15914             if(ax !== undefined && ay !== undefined){
15915                 el.setLeftTop(ax, ay);
15916             }else if(ax !== undefined){
15917                 el.setLeft(ax);
15918             }else if(ay !== undefined){
15919                 el.setTop(ay);
15920             }
15921             this.onPosition(ax, ay);
15922             this.fireEvent('move', this, ax, ay);
15923         }
15924         return this;
15925     },
15926
15927     /**
15928      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15929      * This method fires the move event.
15930      * @param {Number} x The new x position
15931      * @param {Number} y The new y position
15932      * @returns {Roo.BoxComponent} this
15933      */
15934     setPagePosition : function(x, y){
15935         this.pageX = x;
15936         this.pageY = y;
15937         if(!this.boxReady){
15938             return;
15939         }
15940         if(x === undefined || y === undefined){ // cannot translate undefined points
15941             return;
15942         }
15943         var p = this.el.translatePoints(x, y);
15944         this.setPosition(p.left, p.top);
15945         return this;
15946     },
15947
15948     // private
15949     onRender : function(ct, position){
15950         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15951         if(this.resizeEl){
15952             this.resizeEl = Roo.get(this.resizeEl);
15953         }
15954         if(this.positionEl){
15955             this.positionEl = Roo.get(this.positionEl);
15956         }
15957     },
15958
15959     // private
15960     afterRender : function(){
15961         Roo.BoxComponent.superclass.afterRender.call(this);
15962         this.boxReady = true;
15963         this.setSize(this.width, this.height);
15964         if(this.x || this.y){
15965             this.setPosition(this.x, this.y);
15966         }
15967         if(this.pageX || this.pageY){
15968             this.setPagePosition(this.pageX, this.pageY);
15969         }
15970     },
15971
15972     /**
15973      * Force the component's size to recalculate based on the underlying element's current height and width.
15974      * @returns {Roo.BoxComponent} this
15975      */
15976     syncSize : function(){
15977         delete this.lastSize;
15978         this.setSize(this.el.getWidth(), this.el.getHeight());
15979         return this;
15980     },
15981
15982     /**
15983      * Called after the component is resized, this method is empty by default but can be implemented by any
15984      * subclass that needs to perform custom logic after a resize occurs.
15985      * @param {Number} adjWidth The box-adjusted width that was set
15986      * @param {Number} adjHeight The box-adjusted height that was set
15987      * @param {Number} rawWidth The width that was originally specified
15988      * @param {Number} rawHeight The height that was originally specified
15989      */
15990     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15991
15992     },
15993
15994     /**
15995      * Called after the component is moved, this method is empty by default but can be implemented by any
15996      * subclass that needs to perform custom logic after a move occurs.
15997      * @param {Number} x The new x position
15998      * @param {Number} y The new y position
15999      */
16000     onPosition : function(x, y){
16001
16002     },
16003
16004     // private
16005     adjustSize : function(w, h){
16006         if(this.autoWidth){
16007             w = 'auto';
16008         }
16009         if(this.autoHeight){
16010             h = 'auto';
16011         }
16012         return {width : w, height: h};
16013     },
16014
16015     // private
16016     adjustPosition : function(x, y){
16017         return {x : x, y: y};
16018     }
16019 });/*
16020  * Based on:
16021  * Ext JS Library 1.1.1
16022  * Copyright(c) 2006-2007, Ext JS, LLC.
16023  *
16024  * Originally Released Under LGPL - original licence link has changed is not relivant.
16025  *
16026  * Fork - LGPL
16027  * <script type="text/javascript">
16028  */
16029  (function(){ 
16030 /**
16031  * @class Roo.Layer
16032  * @extends Roo.Element
16033  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16034  * automatic maintaining of shadow/shim positions.
16035  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16036  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16037  * you can pass a string with a CSS class name. False turns off the shadow.
16038  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16039  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16040  * @cfg {String} cls CSS class to add to the element
16041  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16042  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16043  * @constructor
16044  * @param {Object} config An object with config options.
16045  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16046  */
16047
16048 Roo.Layer = function(config, existingEl){
16049     config = config || {};
16050     var dh = Roo.DomHelper;
16051     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16052     if(existingEl){
16053         this.dom = Roo.getDom(existingEl);
16054     }
16055     if(!this.dom){
16056         var o = config.dh || {tag: "div", cls: "x-layer"};
16057         this.dom = dh.append(pel, o);
16058     }
16059     if(config.cls){
16060         this.addClass(config.cls);
16061     }
16062     this.constrain = config.constrain !== false;
16063     this.visibilityMode = Roo.Element.VISIBILITY;
16064     if(config.id){
16065         this.id = this.dom.id = config.id;
16066     }else{
16067         this.id = Roo.id(this.dom);
16068     }
16069     this.zindex = config.zindex || this.getZIndex();
16070     this.position("absolute", this.zindex);
16071     if(config.shadow){
16072         this.shadowOffset = config.shadowOffset || 4;
16073         this.shadow = new Roo.Shadow({
16074             offset : this.shadowOffset,
16075             mode : config.shadow
16076         });
16077     }else{
16078         this.shadowOffset = 0;
16079     }
16080     this.useShim = config.shim !== false && Roo.useShims;
16081     this.useDisplay = config.useDisplay;
16082     this.hide();
16083 };
16084
16085 var supr = Roo.Element.prototype;
16086
16087 // shims are shared among layer to keep from having 100 iframes
16088 var shims = [];
16089
16090 Roo.extend(Roo.Layer, Roo.Element, {
16091
16092     getZIndex : function(){
16093         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16094     },
16095
16096     getShim : function(){
16097         if(!this.useShim){
16098             return null;
16099         }
16100         if(this.shim){
16101             return this.shim;
16102         }
16103         var shim = shims.shift();
16104         if(!shim){
16105             shim = this.createShim();
16106             shim.enableDisplayMode('block');
16107             shim.dom.style.display = 'none';
16108             shim.dom.style.visibility = 'visible';
16109         }
16110         var pn = this.dom.parentNode;
16111         if(shim.dom.parentNode != pn){
16112             pn.insertBefore(shim.dom, this.dom);
16113         }
16114         shim.setStyle('z-index', this.getZIndex()-2);
16115         this.shim = shim;
16116         return shim;
16117     },
16118
16119     hideShim : function(){
16120         if(this.shim){
16121             this.shim.setDisplayed(false);
16122             shims.push(this.shim);
16123             delete this.shim;
16124         }
16125     },
16126
16127     disableShadow : function(){
16128         if(this.shadow){
16129             this.shadowDisabled = true;
16130             this.shadow.hide();
16131             this.lastShadowOffset = this.shadowOffset;
16132             this.shadowOffset = 0;
16133         }
16134     },
16135
16136     enableShadow : function(show){
16137         if(this.shadow){
16138             this.shadowDisabled = false;
16139             this.shadowOffset = this.lastShadowOffset;
16140             delete this.lastShadowOffset;
16141             if(show){
16142                 this.sync(true);
16143             }
16144         }
16145     },
16146
16147     // private
16148     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16149     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16150     sync : function(doShow){
16151         var sw = this.shadow;
16152         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16153             var sh = this.getShim();
16154
16155             var w = this.getWidth(),
16156                 h = this.getHeight();
16157
16158             var l = this.getLeft(true),
16159                 t = this.getTop(true);
16160
16161             if(sw && !this.shadowDisabled){
16162                 if(doShow && !sw.isVisible()){
16163                     sw.show(this);
16164                 }else{
16165                     sw.realign(l, t, w, h);
16166                 }
16167                 if(sh){
16168                     if(doShow){
16169                        sh.show();
16170                     }
16171                     // fit the shim behind the shadow, so it is shimmed too
16172                     var a = sw.adjusts, s = sh.dom.style;
16173                     s.left = (Math.min(l, l+a.l))+"px";
16174                     s.top = (Math.min(t, t+a.t))+"px";
16175                     s.width = (w+a.w)+"px";
16176                     s.height = (h+a.h)+"px";
16177                 }
16178             }else if(sh){
16179                 if(doShow){
16180                    sh.show();
16181                 }
16182                 sh.setSize(w, h);
16183                 sh.setLeftTop(l, t);
16184             }
16185             
16186         }
16187     },
16188
16189     // private
16190     destroy : function(){
16191         this.hideShim();
16192         if(this.shadow){
16193             this.shadow.hide();
16194         }
16195         this.removeAllListeners();
16196         var pn = this.dom.parentNode;
16197         if(pn){
16198             pn.removeChild(this.dom);
16199         }
16200         Roo.Element.uncache(this.id);
16201     },
16202
16203     remove : function(){
16204         this.destroy();
16205     },
16206
16207     // private
16208     beginUpdate : function(){
16209         this.updating = true;
16210     },
16211
16212     // private
16213     endUpdate : function(){
16214         this.updating = false;
16215         this.sync(true);
16216     },
16217
16218     // private
16219     hideUnders : function(negOffset){
16220         if(this.shadow){
16221             this.shadow.hide();
16222         }
16223         this.hideShim();
16224     },
16225
16226     // private
16227     constrainXY : function(){
16228         if(this.constrain){
16229             var vw = Roo.lib.Dom.getViewWidth(),
16230                 vh = Roo.lib.Dom.getViewHeight();
16231             var s = Roo.get(document).getScroll();
16232
16233             var xy = this.getXY();
16234             var x = xy[0], y = xy[1];   
16235             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16236             // only move it if it needs it
16237             var moved = false;
16238             // first validate right/bottom
16239             if((x + w) > vw+s.left){
16240                 x = vw - w - this.shadowOffset;
16241                 moved = true;
16242             }
16243             if((y + h) > vh+s.top){
16244                 y = vh - h - this.shadowOffset;
16245                 moved = true;
16246             }
16247             // then make sure top/left isn't negative
16248             if(x < s.left){
16249                 x = s.left;
16250                 moved = true;
16251             }
16252             if(y < s.top){
16253                 y = s.top;
16254                 moved = true;
16255             }
16256             if(moved){
16257                 if(this.avoidY){
16258                     var ay = this.avoidY;
16259                     if(y <= ay && (y+h) >= ay){
16260                         y = ay-h-5;   
16261                     }
16262                 }
16263                 xy = [x, y];
16264                 this.storeXY(xy);
16265                 supr.setXY.call(this, xy);
16266                 this.sync();
16267             }
16268         }
16269     },
16270
16271     isVisible : function(){
16272         return this.visible;    
16273     },
16274
16275     // private
16276     showAction : function(){
16277         this.visible = true; // track visibility to prevent getStyle calls
16278         if(this.useDisplay === true){
16279             this.setDisplayed("");
16280         }else if(this.lastXY){
16281             supr.setXY.call(this, this.lastXY);
16282         }else if(this.lastLT){
16283             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16284         }
16285     },
16286
16287     // private
16288     hideAction : function(){
16289         this.visible = false;
16290         if(this.useDisplay === true){
16291             this.setDisplayed(false);
16292         }else{
16293             this.setLeftTop(-10000,-10000);
16294         }
16295     },
16296
16297     // overridden Element method
16298     setVisible : function(v, a, d, c, e){
16299         if(v){
16300             this.showAction();
16301         }
16302         if(a && v){
16303             var cb = function(){
16304                 this.sync(true);
16305                 if(c){
16306                     c();
16307                 }
16308             }.createDelegate(this);
16309             supr.setVisible.call(this, true, true, d, cb, e);
16310         }else{
16311             if(!v){
16312                 this.hideUnders(true);
16313             }
16314             var cb = c;
16315             if(a){
16316                 cb = function(){
16317                     this.hideAction();
16318                     if(c){
16319                         c();
16320                     }
16321                 }.createDelegate(this);
16322             }
16323             supr.setVisible.call(this, v, a, d, cb, e);
16324             if(v){
16325                 this.sync(true);
16326             }else if(!a){
16327                 this.hideAction();
16328             }
16329         }
16330     },
16331
16332     storeXY : function(xy){
16333         delete this.lastLT;
16334         this.lastXY = xy;
16335     },
16336
16337     storeLeftTop : function(left, top){
16338         delete this.lastXY;
16339         this.lastLT = [left, top];
16340     },
16341
16342     // private
16343     beforeFx : function(){
16344         this.beforeAction();
16345         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16346     },
16347
16348     // private
16349     afterFx : function(){
16350         Roo.Layer.superclass.afterFx.apply(this, arguments);
16351         this.sync(this.isVisible());
16352     },
16353
16354     // private
16355     beforeAction : function(){
16356         if(!this.updating && this.shadow){
16357             this.shadow.hide();
16358         }
16359     },
16360
16361     // overridden Element method
16362     setLeft : function(left){
16363         this.storeLeftTop(left, this.getTop(true));
16364         supr.setLeft.apply(this, arguments);
16365         this.sync();
16366     },
16367
16368     setTop : function(top){
16369         this.storeLeftTop(this.getLeft(true), top);
16370         supr.setTop.apply(this, arguments);
16371         this.sync();
16372     },
16373
16374     setLeftTop : function(left, top){
16375         this.storeLeftTop(left, top);
16376         supr.setLeftTop.apply(this, arguments);
16377         this.sync();
16378     },
16379
16380     setXY : function(xy, a, d, c, e){
16381         this.fixDisplay();
16382         this.beforeAction();
16383         this.storeXY(xy);
16384         var cb = this.createCB(c);
16385         supr.setXY.call(this, xy, a, d, cb, e);
16386         if(!a){
16387             cb();
16388         }
16389     },
16390
16391     // private
16392     createCB : function(c){
16393         var el = this;
16394         return function(){
16395             el.constrainXY();
16396             el.sync(true);
16397             if(c){
16398                 c();
16399             }
16400         };
16401     },
16402
16403     // overridden Element method
16404     setX : function(x, a, d, c, e){
16405         this.setXY([x, this.getY()], a, d, c, e);
16406     },
16407
16408     // overridden Element method
16409     setY : function(y, a, d, c, e){
16410         this.setXY([this.getX(), y], a, d, c, e);
16411     },
16412
16413     // overridden Element method
16414     setSize : function(w, h, a, d, c, e){
16415         this.beforeAction();
16416         var cb = this.createCB(c);
16417         supr.setSize.call(this, w, h, a, d, cb, e);
16418         if(!a){
16419             cb();
16420         }
16421     },
16422
16423     // overridden Element method
16424     setWidth : function(w, a, d, c, e){
16425         this.beforeAction();
16426         var cb = this.createCB(c);
16427         supr.setWidth.call(this, w, a, d, cb, e);
16428         if(!a){
16429             cb();
16430         }
16431     },
16432
16433     // overridden Element method
16434     setHeight : function(h, a, d, c, e){
16435         this.beforeAction();
16436         var cb = this.createCB(c);
16437         supr.setHeight.call(this, h, a, d, cb, e);
16438         if(!a){
16439             cb();
16440         }
16441     },
16442
16443     // overridden Element method
16444     setBounds : function(x, y, w, h, a, d, c, e){
16445         this.beforeAction();
16446         var cb = this.createCB(c);
16447         if(!a){
16448             this.storeXY([x, y]);
16449             supr.setXY.call(this, [x, y]);
16450             supr.setSize.call(this, w, h, a, d, cb, e);
16451             cb();
16452         }else{
16453             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16454         }
16455         return this;
16456     },
16457     
16458     /**
16459      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16460      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16461      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16462      * @param {Number} zindex The new z-index to set
16463      * @return {this} The Layer
16464      */
16465     setZIndex : function(zindex){
16466         this.zindex = zindex;
16467         this.setStyle("z-index", zindex + 2);
16468         if(this.shadow){
16469             this.shadow.setZIndex(zindex + 1);
16470         }
16471         if(this.shim){
16472             this.shim.setStyle("z-index", zindex);
16473         }
16474     }
16475 });
16476 })();/*
16477  * Original code for Roojs - LGPL
16478  * <script type="text/javascript">
16479  */
16480  
16481 /**
16482  * @class Roo.XComponent
16483  * A delayed Element creator...
16484  * Or a way to group chunks of interface together.
16485  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16486  *  used in conjunction with XComponent.build() it will create an instance of each element,
16487  *  then call addxtype() to build the User interface.
16488  * 
16489  * Mypart.xyx = new Roo.XComponent({
16490
16491     parent : 'Mypart.xyz', // empty == document.element.!!
16492     order : '001',
16493     name : 'xxxx'
16494     region : 'xxxx'
16495     disabled : function() {} 
16496      
16497     tree : function() { // return an tree of xtype declared components
16498         var MODULE = this;
16499         return 
16500         {
16501             xtype : 'NestedLayoutPanel',
16502             // technicall
16503         }
16504      ]
16505  *})
16506  *
16507  *
16508  * It can be used to build a big heiracy, with parent etc.
16509  * or you can just use this to render a single compoent to a dom element
16510  * MYPART.render(Roo.Element | String(id) | dom_element )
16511  *
16512  *
16513  * Usage patterns.
16514  *
16515  * Classic Roo
16516  *
16517  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16518  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16519  *
16520  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16521  *
16522  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16523  * - if mulitple topModules exist, the last one is defined as the top module.
16524  *
16525  * Embeded Roo
16526  * 
16527  * When the top level or multiple modules are to embedded into a existing HTML page,
16528  * the parent element can container '#id' of the element where the module will be drawn.
16529  *
16530  * Bootstrap Roo
16531  *
16532  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16533  * it relies more on a include mechanism, where sub modules are included into an outer page.
16534  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16535  * 
16536  * Bootstrap Roo Included elements
16537  *
16538  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16539  * hence confusing the component builder as it thinks there are multiple top level elements. 
16540  *
16541  * String Over-ride & Translations
16542  *
16543  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16544  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16545  * are needed. @see Roo.XComponent.overlayString  
16546  * 
16547  * 
16548  * 
16549  * @extends Roo.util.Observable
16550  * @constructor
16551  * @param cfg {Object} configuration of component
16552  * 
16553  */
16554 Roo.XComponent = function(cfg) {
16555     Roo.apply(this, cfg);
16556     this.addEvents({ 
16557         /**
16558              * @event built
16559              * Fires when this the componnt is built
16560              * @param {Roo.XComponent} c the component
16561              */
16562         'built' : true
16563         
16564     });
16565     this.region = this.region || 'center'; // default..
16566     Roo.XComponent.register(this);
16567     this.modules = false;
16568     this.el = false; // where the layout goes..
16569     
16570     
16571 }
16572 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16573     /**
16574      * @property el
16575      * The created element (with Roo.factory())
16576      * @type {Roo.Layout}
16577      */
16578     el  : false,
16579     
16580     /**
16581      * @property el
16582      * for BC  - use el in new code
16583      * @type {Roo.Layout}
16584      */
16585     panel : false,
16586     
16587     /**
16588      * @property layout
16589      * for BC  - use el in new code
16590      * @type {Roo.Layout}
16591      */
16592     layout : false,
16593     
16594      /**
16595      * @cfg {Function|boolean} disabled
16596      * If this module is disabled by some rule, return true from the funtion
16597      */
16598     disabled : false,
16599     
16600     /**
16601      * @cfg {String} parent 
16602      * Name of parent element which it get xtype added to..
16603      */
16604     parent: false,
16605     
16606     /**
16607      * @cfg {String} order
16608      * Used to set the order in which elements are created (usefull for multiple tabs)
16609      */
16610     
16611     order : false,
16612     /**
16613      * @cfg {String} name
16614      * String to display while loading.
16615      */
16616     name : false,
16617     /**
16618      * @cfg {String} region
16619      * Region to render component to (defaults to center)
16620      */
16621     region : 'center',
16622     
16623     /**
16624      * @cfg {Array} items
16625      * A single item array - the first element is the root of the tree..
16626      * It's done this way to stay compatible with the Xtype system...
16627      */
16628     items : false,
16629     
16630     /**
16631      * @property _tree
16632      * The method that retuns the tree of parts that make up this compoennt 
16633      * @type {function}
16634      */
16635     _tree  : false,
16636     
16637      /**
16638      * render
16639      * render element to dom or tree
16640      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16641      */
16642     
16643     render : function(el)
16644     {
16645         
16646         el = el || false;
16647         var hp = this.parent ? 1 : 0;
16648         Roo.debug &&  Roo.log(this);
16649         
16650         var tree = this._tree ? this._tree() : this.tree();
16651
16652         
16653         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16654             // if parent is a '#.....' string, then let's use that..
16655             var ename = this.parent.substr(1);
16656             this.parent = false;
16657             Roo.debug && Roo.log(ename);
16658             switch (ename) {
16659                 case 'bootstrap-body':
16660                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16661                         // this is the BorderLayout standard?
16662                        this.parent = { el : true };
16663                        break;
16664                     }
16665                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16666                         // need to insert stuff...
16667                         this.parent =  {
16668                              el : new Roo.bootstrap.layout.Border({
16669                                  el : document.body, 
16670                      
16671                                  center: {
16672                                     titlebar: false,
16673                                     autoScroll:false,
16674                                     closeOnTab: true,
16675                                     tabPosition: 'top',
16676                                       //resizeTabs: true,
16677                                     alwaysShowTabs: true,
16678                                     hideTabs: false
16679                                      //minTabWidth: 140
16680                                  }
16681                              })
16682                         
16683                          };
16684                          break;
16685                     }
16686                          
16687                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16688                         this.parent = { el :  new  Roo.bootstrap.Body() };
16689                         Roo.debug && Roo.log("setting el to doc body");
16690                          
16691                     } else {
16692                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16693                     }
16694                     break;
16695                 case 'bootstrap':
16696                     this.parent = { el : true};
16697                     // fall through
16698                 default:
16699                     el = Roo.get(ename);
16700                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16701                         this.parent = { el : true};
16702                     }
16703                     
16704                     break;
16705             }
16706                 
16707             
16708             if (!el && !this.parent) {
16709                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16710                 return;
16711             }
16712         }
16713         
16714         Roo.debug && Roo.log("EL:");
16715         Roo.debug && Roo.log(el);
16716         Roo.debug && Roo.log("this.parent.el:");
16717         Roo.debug && Roo.log(this.parent.el);
16718         
16719
16720         // altertive root elements ??? - we need a better way to indicate these.
16721         var is_alt = Roo.XComponent.is_alt ||
16722                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16723                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16724                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16725         
16726         
16727         
16728         if (!this.parent && is_alt) {
16729             //el = Roo.get(document.body);
16730             this.parent = { el : true };
16731         }
16732             
16733             
16734         
16735         if (!this.parent) {
16736             
16737             Roo.debug && Roo.log("no parent - creating one");
16738             
16739             el = el ? Roo.get(el) : false;      
16740             
16741             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16742                 
16743                 this.parent =  {
16744                     el : new Roo.bootstrap.layout.Border({
16745                         el: el || document.body,
16746                     
16747                         center: {
16748                             titlebar: false,
16749                             autoScroll:false,
16750                             closeOnTab: true,
16751                             tabPosition: 'top',
16752                              //resizeTabs: true,
16753                             alwaysShowTabs: false,
16754                             hideTabs: true,
16755                             minTabWidth: 140,
16756                             overflow: 'visible'
16757                          }
16758                      })
16759                 };
16760             } else {
16761             
16762                 // it's a top level one..
16763                 this.parent =  {
16764                     el : new Roo.BorderLayout(el || document.body, {
16765                         center: {
16766                             titlebar: false,
16767                             autoScroll:false,
16768                             closeOnTab: true,
16769                             tabPosition: 'top',
16770                              //resizeTabs: true,
16771                             alwaysShowTabs: el && hp? false :  true,
16772                             hideTabs: el || !hp ? true :  false,
16773                             minTabWidth: 140
16774                          }
16775                     })
16776                 };
16777             }
16778         }
16779         
16780         if (!this.parent.el) {
16781                 // probably an old style ctor, which has been disabled.
16782                 return;
16783
16784         }
16785                 // The 'tree' method is  '_tree now' 
16786             
16787         tree.region = tree.region || this.region;
16788         var is_body = false;
16789         if (this.parent.el === true) {
16790             // bootstrap... - body..
16791             if (el) {
16792                 tree.el = el;
16793             }
16794             this.parent.el = Roo.factory(tree);
16795             is_body = true;
16796         }
16797         
16798         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16799         this.fireEvent('built', this);
16800         
16801         this.panel = this.el;
16802         this.layout = this.panel.layout;
16803         this.parentLayout = this.parent.layout  || false;  
16804          
16805     }
16806     
16807 });
16808
16809 Roo.apply(Roo.XComponent, {
16810     /**
16811      * @property  hideProgress
16812      * true to disable the building progress bar.. usefull on single page renders.
16813      * @type Boolean
16814      */
16815     hideProgress : false,
16816     /**
16817      * @property  buildCompleted
16818      * True when the builder has completed building the interface.
16819      * @type Boolean
16820      */
16821     buildCompleted : false,
16822      
16823     /**
16824      * @property  topModule
16825      * the upper most module - uses document.element as it's constructor.
16826      * @type Object
16827      */
16828      
16829     topModule  : false,
16830       
16831     /**
16832      * @property  modules
16833      * array of modules to be created by registration system.
16834      * @type {Array} of Roo.XComponent
16835      */
16836     
16837     modules : [],
16838     /**
16839      * @property  elmodules
16840      * array of modules to be created by which use #ID 
16841      * @type {Array} of Roo.XComponent
16842      */
16843      
16844     elmodules : [],
16845
16846      /**
16847      * @property  is_alt
16848      * Is an alternative Root - normally used by bootstrap or other systems,
16849      *    where the top element in the tree can wrap 'body' 
16850      * @type {boolean}  (default false)
16851      */
16852      
16853     is_alt : false,
16854     /**
16855      * @property  build_from_html
16856      * Build elements from html - used by bootstrap HTML stuff 
16857      *    - this is cleared after build is completed
16858      * @type {boolean}    (default false)
16859      */
16860      
16861     build_from_html : false,
16862     /**
16863      * Register components to be built later.
16864      *
16865      * This solves the following issues
16866      * - Building is not done on page load, but after an authentication process has occured.
16867      * - Interface elements are registered on page load
16868      * - Parent Interface elements may not be loaded before child, so this handles that..
16869      * 
16870      *
16871      * example:
16872      * 
16873      * MyApp.register({
16874           order : '000001',
16875           module : 'Pman.Tab.projectMgr',
16876           region : 'center',
16877           parent : 'Pman.layout',
16878           disabled : false,  // or use a function..
16879         })
16880      
16881      * * @param {Object} details about module
16882      */
16883     register : function(obj) {
16884                 
16885         Roo.XComponent.event.fireEvent('register', obj);
16886         switch(typeof(obj.disabled) ) {
16887                 
16888             case 'undefined':
16889                 break;
16890             
16891             case 'function':
16892                 if ( obj.disabled() ) {
16893                         return;
16894                 }
16895                 break;
16896             
16897             default:
16898                 if (obj.disabled || obj.region == '#disabled') {
16899                         return;
16900                 }
16901                 break;
16902         }
16903                 
16904         this.modules.push(obj);
16905          
16906     },
16907     /**
16908      * convert a string to an object..
16909      * eg. 'AAA.BBB' -> finds AAA.BBB
16910
16911      */
16912     
16913     toObject : function(str)
16914     {
16915         if (!str || typeof(str) == 'object') {
16916             return str;
16917         }
16918         if (str.substring(0,1) == '#') {
16919             return str;
16920         }
16921
16922         var ar = str.split('.');
16923         var rt, o;
16924         rt = ar.shift();
16925             /** eval:var:o */
16926         try {
16927             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16928         } catch (e) {
16929             throw "Module not found : " + str;
16930         }
16931         
16932         if (o === false) {
16933             throw "Module not found : " + str;
16934         }
16935         Roo.each(ar, function(e) {
16936             if (typeof(o[e]) == 'undefined') {
16937                 throw "Module not found : " + str;
16938             }
16939             o = o[e];
16940         });
16941         
16942         return o;
16943         
16944     },
16945     
16946     
16947     /**
16948      * move modules into their correct place in the tree..
16949      * 
16950      */
16951     preBuild : function ()
16952     {
16953         var _t = this;
16954         Roo.each(this.modules , function (obj)
16955         {
16956             Roo.XComponent.event.fireEvent('beforebuild', obj);
16957             
16958             var opar = obj.parent;
16959             try { 
16960                 obj.parent = this.toObject(opar);
16961             } catch(e) {
16962                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16963                 return;
16964             }
16965             
16966             if (!obj.parent) {
16967                 Roo.debug && Roo.log("GOT top level module");
16968                 Roo.debug && Roo.log(obj);
16969                 obj.modules = new Roo.util.MixedCollection(false, 
16970                     function(o) { return o.order + '' }
16971                 );
16972                 this.topModule = obj;
16973                 return;
16974             }
16975                         // parent is a string (usually a dom element name..)
16976             if (typeof(obj.parent) == 'string') {
16977                 this.elmodules.push(obj);
16978                 return;
16979             }
16980             if (obj.parent.constructor != Roo.XComponent) {
16981                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16982             }
16983             if (!obj.parent.modules) {
16984                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16985                     function(o) { return o.order + '' }
16986                 );
16987             }
16988             if (obj.parent.disabled) {
16989                 obj.disabled = true;
16990             }
16991             obj.parent.modules.add(obj);
16992         }, this);
16993     },
16994     
16995      /**
16996      * make a list of modules to build.
16997      * @return {Array} list of modules. 
16998      */ 
16999     
17000     buildOrder : function()
17001     {
17002         var _this = this;
17003         var cmp = function(a,b) {   
17004             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17005         };
17006         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17007             throw "No top level modules to build";
17008         }
17009         
17010         // make a flat list in order of modules to build.
17011         var mods = this.topModule ? [ this.topModule ] : [];
17012                 
17013         
17014         // elmodules (is a list of DOM based modules )
17015         Roo.each(this.elmodules, function(e) {
17016             mods.push(e);
17017             if (!this.topModule &&
17018                 typeof(e.parent) == 'string' &&
17019                 e.parent.substring(0,1) == '#' &&
17020                 Roo.get(e.parent.substr(1))
17021                ) {
17022                 
17023                 _this.topModule = e;
17024             }
17025             
17026         });
17027
17028         
17029         // add modules to their parents..
17030         var addMod = function(m) {
17031             Roo.debug && Roo.log("build Order: add: " + m.name);
17032                 
17033             mods.push(m);
17034             if (m.modules && !m.disabled) {
17035                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17036                 m.modules.keySort('ASC',  cmp );
17037                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17038     
17039                 m.modules.each(addMod);
17040             } else {
17041                 Roo.debug && Roo.log("build Order: no child modules");
17042             }
17043             // not sure if this is used any more..
17044             if (m.finalize) {
17045                 m.finalize.name = m.name + " (clean up) ";
17046                 mods.push(m.finalize);
17047             }
17048             
17049         }
17050         if (this.topModule && this.topModule.modules) { 
17051             this.topModule.modules.keySort('ASC',  cmp );
17052             this.topModule.modules.each(addMod);
17053         } 
17054         return mods;
17055     },
17056     
17057      /**
17058      * Build the registered modules.
17059      * @param {Object} parent element.
17060      * @param {Function} optional method to call after module has been added.
17061      * 
17062      */ 
17063    
17064     build : function(opts) 
17065     {
17066         
17067         if (typeof(opts) != 'undefined') {
17068             Roo.apply(this,opts);
17069         }
17070         
17071         this.preBuild();
17072         var mods = this.buildOrder();
17073       
17074         //this.allmods = mods;
17075         //Roo.debug && Roo.log(mods);
17076         //return;
17077         if (!mods.length) { // should not happen
17078             throw "NO modules!!!";
17079         }
17080         
17081         
17082         var msg = "Building Interface...";
17083         // flash it up as modal - so we store the mask!?
17084         if (!this.hideProgress && Roo.MessageBox) {
17085             Roo.MessageBox.show({ title: 'loading' });
17086             Roo.MessageBox.show({
17087                title: "Please wait...",
17088                msg: msg,
17089                width:450,
17090                progress:true,
17091                buttons : false,
17092                closable:false,
17093                modal: false
17094               
17095             });
17096         }
17097         var total = mods.length;
17098         
17099         var _this = this;
17100         var progressRun = function() {
17101             if (!mods.length) {
17102                 Roo.debug && Roo.log('hide?');
17103                 if (!this.hideProgress && Roo.MessageBox) {
17104                     Roo.MessageBox.hide();
17105                 }
17106                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17107                 
17108                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17109                 
17110                 // THE END...
17111                 return false;   
17112             }
17113             
17114             var m = mods.shift();
17115             
17116             
17117             Roo.debug && Roo.log(m);
17118             // not sure if this is supported any more.. - modules that are are just function
17119             if (typeof(m) == 'function') { 
17120                 m.call(this);
17121                 return progressRun.defer(10, _this);
17122             } 
17123             
17124             
17125             msg = "Building Interface " + (total  - mods.length) + 
17126                     " of " + total + 
17127                     (m.name ? (' - ' + m.name) : '');
17128                         Roo.debug && Roo.log(msg);
17129             if (!_this.hideProgress &&  Roo.MessageBox) { 
17130                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17131             }
17132             
17133          
17134             // is the module disabled?
17135             var disabled = (typeof(m.disabled) == 'function') ?
17136                 m.disabled.call(m.module.disabled) : m.disabled;    
17137             
17138             
17139             if (disabled) {
17140                 return progressRun(); // we do not update the display!
17141             }
17142             
17143             // now build 
17144             
17145                         
17146                         
17147             m.render();
17148             // it's 10 on top level, and 1 on others??? why...
17149             return progressRun.defer(10, _this);
17150              
17151         }
17152         progressRun.defer(1, _this);
17153      
17154         
17155         
17156     },
17157     /**
17158      * Overlay a set of modified strings onto a component
17159      * This is dependant on our builder exporting the strings and 'named strings' elements.
17160      * 
17161      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17162      * @param {Object} associative array of 'named' string and it's new value.
17163      * 
17164      */
17165         overlayStrings : function( component, strings )
17166     {
17167         if (typeof(component['_named_strings']) == 'undefined') {
17168             throw "ERROR: component does not have _named_strings";
17169         }
17170         for ( var k in strings ) {
17171             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17172             if (md !== false) {
17173                 component['_strings'][md] = strings[k];
17174             } else {
17175                 Roo.log('could not find named string: ' + k + ' in');
17176                 Roo.log(component);
17177             }
17178             
17179         }
17180         
17181     },
17182     
17183         
17184         /**
17185          * Event Object.
17186          *
17187          *
17188          */
17189         event: false, 
17190     /**
17191          * wrapper for event.on - aliased later..  
17192          * Typically use to register a event handler for register:
17193          *
17194          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17195          *
17196          */
17197     on : false
17198    
17199     
17200     
17201 });
17202
17203 Roo.XComponent.event = new Roo.util.Observable({
17204                 events : { 
17205                         /**
17206                          * @event register
17207                          * Fires when an Component is registered,
17208                          * set the disable property on the Component to stop registration.
17209                          * @param {Roo.XComponent} c the component being registerd.
17210                          * 
17211                          */
17212                         'register' : true,
17213             /**
17214                          * @event beforebuild
17215                          * Fires before each Component is built
17216                          * can be used to apply permissions.
17217                          * @param {Roo.XComponent} c the component being registerd.
17218                          * 
17219                          */
17220                         'beforebuild' : true,
17221                         /**
17222                          * @event buildcomplete
17223                          * Fires on the top level element when all elements have been built
17224                          * @param {Roo.XComponent} the top level component.
17225                          */
17226                         'buildcomplete' : true
17227                         
17228                 }
17229 });
17230
17231 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17232  //
17233  /**
17234  * marked - a markdown parser
17235  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17236  * https://github.com/chjj/marked
17237  */
17238
17239
17240 /**
17241  *
17242  * Roo.Markdown - is a very crude wrapper around marked..
17243  *
17244  * usage:
17245  * 
17246  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17247  * 
17248  * Note: move the sample code to the bottom of this
17249  * file before uncommenting it.
17250  *
17251  */
17252
17253 Roo.Markdown = {};
17254 Roo.Markdown.toHtml = function(text) {
17255     
17256     var c = new Roo.Markdown.marked.setOptions({
17257             renderer: new Roo.Markdown.marked.Renderer(),
17258             gfm: true,
17259             tables: true,
17260             breaks: false,
17261             pedantic: false,
17262             sanitize: false,
17263             smartLists: true,
17264             smartypants: false
17265           });
17266     // A FEW HACKS!!?
17267     
17268     text = text.replace(/\\\n/g,' ');
17269     return Roo.Markdown.marked(text);
17270 };
17271 //
17272 // converter
17273 //
17274 // Wraps all "globals" so that the only thing
17275 // exposed is makeHtml().
17276 //
17277 (function() {
17278     
17279      /**
17280          * eval:var:escape
17281          * eval:var:unescape
17282          * eval:var:replace
17283          */
17284       
17285     /**
17286      * Helpers
17287      */
17288     
17289     var escape = function (html, encode) {
17290       return html
17291         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17292         .replace(/</g, '&lt;')
17293         .replace(/>/g, '&gt;')
17294         .replace(/"/g, '&quot;')
17295         .replace(/'/g, '&#39;');
17296     }
17297     
17298     var unescape = function (html) {
17299         // explicitly match decimal, hex, and named HTML entities 
17300       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17301         n = n.toLowerCase();
17302         if (n === 'colon') { return ':'; }
17303         if (n.charAt(0) === '#') {
17304           return n.charAt(1) === 'x'
17305             ? String.fromCharCode(parseInt(n.substring(2), 16))
17306             : String.fromCharCode(+n.substring(1));
17307         }
17308         return '';
17309       });
17310     }
17311     
17312     var replace = function (regex, opt) {
17313       regex = regex.source;
17314       opt = opt || '';
17315       return function self(name, val) {
17316         if (!name) { return new RegExp(regex, opt); }
17317         val = val.source || val;
17318         val = val.replace(/(^|[^\[])\^/g, '$1');
17319         regex = regex.replace(name, val);
17320         return self;
17321       };
17322     }
17323
17324
17325          /**
17326          * eval:var:noop
17327     */
17328     var noop = function () {}
17329     noop.exec = noop;
17330     
17331          /**
17332          * eval:var:merge
17333     */
17334     var merge = function (obj) {
17335       var i = 1
17336         , target
17337         , key;
17338     
17339       for (; i < arguments.length; i++) {
17340         target = arguments[i];
17341         for (key in target) {
17342           if (Object.prototype.hasOwnProperty.call(target, key)) {
17343             obj[key] = target[key];
17344           }
17345         }
17346       }
17347     
17348       return obj;
17349     }
17350     
17351     
17352     /**
17353      * Block-Level Grammar
17354      */
17355     
17356     
17357     
17358     
17359     var block = {
17360       newline: /^\n+/,
17361       code: /^( {4}[^\n]+\n*)+/,
17362       fences: noop,
17363       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17364       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17365       nptable: noop,
17366       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17367       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17368       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17369       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17370       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17371       table: noop,
17372       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17373       text: /^[^\n]+/
17374     };
17375     
17376     block.bullet = /(?:[*+-]|\d+\.)/;
17377     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17378     block.item = replace(block.item, 'gm')
17379       (/bull/g, block.bullet)
17380       ();
17381     
17382     block.list = replace(block.list)
17383       (/bull/g, block.bullet)
17384       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17385       ('def', '\\n+(?=' + block.def.source + ')')
17386       ();
17387     
17388     block.blockquote = replace(block.blockquote)
17389       ('def', block.def)
17390       ();
17391     
17392     block._tag = '(?!(?:'
17393       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17394       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17395       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17396     
17397     block.html = replace(block.html)
17398       ('comment', /<!--[\s\S]*?-->/)
17399       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17400       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17401       (/tag/g, block._tag)
17402       ();
17403     
17404     block.paragraph = replace(block.paragraph)
17405       ('hr', block.hr)
17406       ('heading', block.heading)
17407       ('lheading', block.lheading)
17408       ('blockquote', block.blockquote)
17409       ('tag', '<' + block._tag)
17410       ('def', block.def)
17411       ();
17412     
17413     /**
17414      * Normal Block Grammar
17415      */
17416     
17417     block.normal = merge({}, block);
17418     
17419     /**
17420      * GFM Block Grammar
17421      */
17422     
17423     block.gfm = merge({}, block.normal, {
17424       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17425       paragraph: /^/,
17426       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17427     });
17428     
17429     block.gfm.paragraph = replace(block.paragraph)
17430       ('(?!', '(?!'
17431         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17432         + block.list.source.replace('\\1', '\\3') + '|')
17433       ();
17434     
17435     /**
17436      * GFM + Tables Block Grammar
17437      */
17438     
17439     block.tables = merge({}, block.gfm, {
17440       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17441       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17442     });
17443     
17444     /**
17445      * Block Lexer
17446      */
17447     
17448     var Lexer = function (options) {
17449       this.tokens = [];
17450       this.tokens.links = {};
17451       this.options = options || marked.defaults;
17452       this.rules = block.normal;
17453     
17454       if (this.options.gfm) {
17455         if (this.options.tables) {
17456           this.rules = block.tables;
17457         } else {
17458           this.rules = block.gfm;
17459         }
17460       }
17461     }
17462     
17463     /**
17464      * Expose Block Rules
17465      */
17466     
17467     Lexer.rules = block;
17468     
17469     /**
17470      * Static Lex Method
17471      */
17472     
17473     Lexer.lex = function(src, options) {
17474       var lexer = new Lexer(options);
17475       return lexer.lex(src);
17476     };
17477     
17478     /**
17479      * Preprocessing
17480      */
17481     
17482     Lexer.prototype.lex = function(src) {
17483       src = src
17484         .replace(/\r\n|\r/g, '\n')
17485         .replace(/\t/g, '    ')
17486         .replace(/\u00a0/g, ' ')
17487         .replace(/\u2424/g, '\n');
17488     
17489       return this.token(src, true);
17490     };
17491     
17492     /**
17493      * Lexing
17494      */
17495     
17496     Lexer.prototype.token = function(src, top, bq) {
17497       var src = src.replace(/^ +$/gm, '')
17498         , next
17499         , loose
17500         , cap
17501         , bull
17502         , b
17503         , item
17504         , space
17505         , i
17506         , l;
17507     
17508       while (src) {
17509         // newline
17510         if (cap = this.rules.newline.exec(src)) {
17511           src = src.substring(cap[0].length);
17512           if (cap[0].length > 1) {
17513             this.tokens.push({
17514               type: 'space'
17515             });
17516           }
17517         }
17518     
17519         // code
17520         if (cap = this.rules.code.exec(src)) {
17521           src = src.substring(cap[0].length);
17522           cap = cap[0].replace(/^ {4}/gm, '');
17523           this.tokens.push({
17524             type: 'code',
17525             text: !this.options.pedantic
17526               ? cap.replace(/\n+$/, '')
17527               : cap
17528           });
17529           continue;
17530         }
17531     
17532         // fences (gfm)
17533         if (cap = this.rules.fences.exec(src)) {
17534           src = src.substring(cap[0].length);
17535           this.tokens.push({
17536             type: 'code',
17537             lang: cap[2],
17538             text: cap[3] || ''
17539           });
17540           continue;
17541         }
17542     
17543         // heading
17544         if (cap = this.rules.heading.exec(src)) {
17545           src = src.substring(cap[0].length);
17546           this.tokens.push({
17547             type: 'heading',
17548             depth: cap[1].length,
17549             text: cap[2]
17550           });
17551           continue;
17552         }
17553     
17554         // table no leading pipe (gfm)
17555         if (top && (cap = this.rules.nptable.exec(src))) {
17556           src = src.substring(cap[0].length);
17557     
17558           item = {
17559             type: 'table',
17560             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17561             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17562             cells: cap[3].replace(/\n$/, '').split('\n')
17563           };
17564     
17565           for (i = 0; i < item.align.length; i++) {
17566             if (/^ *-+: *$/.test(item.align[i])) {
17567               item.align[i] = 'right';
17568             } else if (/^ *:-+: *$/.test(item.align[i])) {
17569               item.align[i] = 'center';
17570             } else if (/^ *:-+ *$/.test(item.align[i])) {
17571               item.align[i] = 'left';
17572             } else {
17573               item.align[i] = null;
17574             }
17575           }
17576     
17577           for (i = 0; i < item.cells.length; i++) {
17578             item.cells[i] = item.cells[i].split(/ *\| */);
17579           }
17580     
17581           this.tokens.push(item);
17582     
17583           continue;
17584         }
17585     
17586         // lheading
17587         if (cap = this.rules.lheading.exec(src)) {
17588           src = src.substring(cap[0].length);
17589           this.tokens.push({
17590             type: 'heading',
17591             depth: cap[2] === '=' ? 1 : 2,
17592             text: cap[1]
17593           });
17594           continue;
17595         }
17596     
17597         // hr
17598         if (cap = this.rules.hr.exec(src)) {
17599           src = src.substring(cap[0].length);
17600           this.tokens.push({
17601             type: 'hr'
17602           });
17603           continue;
17604         }
17605     
17606         // blockquote
17607         if (cap = this.rules.blockquote.exec(src)) {
17608           src = src.substring(cap[0].length);
17609     
17610           this.tokens.push({
17611             type: 'blockquote_start'
17612           });
17613     
17614           cap = cap[0].replace(/^ *> ?/gm, '');
17615     
17616           // Pass `top` to keep the current
17617           // "toplevel" state. This is exactly
17618           // how markdown.pl works.
17619           this.token(cap, top, true);
17620     
17621           this.tokens.push({
17622             type: 'blockquote_end'
17623           });
17624     
17625           continue;
17626         }
17627     
17628         // list
17629         if (cap = this.rules.list.exec(src)) {
17630           src = src.substring(cap[0].length);
17631           bull = cap[2];
17632     
17633           this.tokens.push({
17634             type: 'list_start',
17635             ordered: bull.length > 1
17636           });
17637     
17638           // Get each top-level item.
17639           cap = cap[0].match(this.rules.item);
17640     
17641           next = false;
17642           l = cap.length;
17643           i = 0;
17644     
17645           for (; i < l; i++) {
17646             item = cap[i];
17647     
17648             // Remove the list item's bullet
17649             // so it is seen as the next token.
17650             space = item.length;
17651             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17652     
17653             // Outdent whatever the
17654             // list item contains. Hacky.
17655             if (~item.indexOf('\n ')) {
17656               space -= item.length;
17657               item = !this.options.pedantic
17658                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17659                 : item.replace(/^ {1,4}/gm, '');
17660             }
17661     
17662             // Determine whether the next list item belongs here.
17663             // Backpedal if it does not belong in this list.
17664             if (this.options.smartLists && i !== l - 1) {
17665               b = block.bullet.exec(cap[i + 1])[0];
17666               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17667                 src = cap.slice(i + 1).join('\n') + src;
17668                 i = l - 1;
17669               }
17670             }
17671     
17672             // Determine whether item is loose or not.
17673             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17674             // for discount behavior.
17675             loose = next || /\n\n(?!\s*$)/.test(item);
17676             if (i !== l - 1) {
17677               next = item.charAt(item.length - 1) === '\n';
17678               if (!loose) { loose = next; }
17679             }
17680     
17681             this.tokens.push({
17682               type: loose
17683                 ? 'loose_item_start'
17684                 : 'list_item_start'
17685             });
17686     
17687             // Recurse.
17688             this.token(item, false, bq);
17689     
17690             this.tokens.push({
17691               type: 'list_item_end'
17692             });
17693           }
17694     
17695           this.tokens.push({
17696             type: 'list_end'
17697           });
17698     
17699           continue;
17700         }
17701     
17702         // html
17703         if (cap = this.rules.html.exec(src)) {
17704           src = src.substring(cap[0].length);
17705           this.tokens.push({
17706             type: this.options.sanitize
17707               ? 'paragraph'
17708               : 'html',
17709             pre: !this.options.sanitizer
17710               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17711             text: cap[0]
17712           });
17713           continue;
17714         }
17715     
17716         // def
17717         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17718           src = src.substring(cap[0].length);
17719           this.tokens.links[cap[1].toLowerCase()] = {
17720             href: cap[2],
17721             title: cap[3]
17722           };
17723           continue;
17724         }
17725     
17726         // table (gfm)
17727         if (top && (cap = this.rules.table.exec(src))) {
17728           src = src.substring(cap[0].length);
17729     
17730           item = {
17731             type: 'table',
17732             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17733             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17734             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17735           };
17736     
17737           for (i = 0; i < item.align.length; i++) {
17738             if (/^ *-+: *$/.test(item.align[i])) {
17739               item.align[i] = 'right';
17740             } else if (/^ *:-+: *$/.test(item.align[i])) {
17741               item.align[i] = 'center';
17742             } else if (/^ *:-+ *$/.test(item.align[i])) {
17743               item.align[i] = 'left';
17744             } else {
17745               item.align[i] = null;
17746             }
17747           }
17748     
17749           for (i = 0; i < item.cells.length; i++) {
17750             item.cells[i] = item.cells[i]
17751               .replace(/^ *\| *| *\| *$/g, '')
17752               .split(/ *\| */);
17753           }
17754     
17755           this.tokens.push(item);
17756     
17757           continue;
17758         }
17759     
17760         // top-level paragraph
17761         if (top && (cap = this.rules.paragraph.exec(src))) {
17762           src = src.substring(cap[0].length);
17763           this.tokens.push({
17764             type: 'paragraph',
17765             text: cap[1].charAt(cap[1].length - 1) === '\n'
17766               ? cap[1].slice(0, -1)
17767               : cap[1]
17768           });
17769           continue;
17770         }
17771     
17772         // text
17773         if (cap = this.rules.text.exec(src)) {
17774           // Top-level should never reach here.
17775           src = src.substring(cap[0].length);
17776           this.tokens.push({
17777             type: 'text',
17778             text: cap[0]
17779           });
17780           continue;
17781         }
17782     
17783         if (src) {
17784           throw new
17785             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17786         }
17787       }
17788     
17789       return this.tokens;
17790     };
17791     
17792     /**
17793      * Inline-Level Grammar
17794      */
17795     
17796     var inline = {
17797       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17798       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17799       url: noop,
17800       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17801       link: /^!?\[(inside)\]\(href\)/,
17802       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17803       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17804       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17805       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17806       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17807       br: /^ {2,}\n(?!\s*$)/,
17808       del: noop,
17809       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17810     };
17811     
17812     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17813     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17814     
17815     inline.link = replace(inline.link)
17816       ('inside', inline._inside)
17817       ('href', inline._href)
17818       ();
17819     
17820     inline.reflink = replace(inline.reflink)
17821       ('inside', inline._inside)
17822       ();
17823     
17824     /**
17825      * Normal Inline Grammar
17826      */
17827     
17828     inline.normal = merge({}, inline);
17829     
17830     /**
17831      * Pedantic Inline Grammar
17832      */
17833     
17834     inline.pedantic = merge({}, inline.normal, {
17835       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17836       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17837     });
17838     
17839     /**
17840      * GFM Inline Grammar
17841      */
17842     
17843     inline.gfm = merge({}, inline.normal, {
17844       escape: replace(inline.escape)('])', '~|])')(),
17845       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17846       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17847       text: replace(inline.text)
17848         (']|', '~]|')
17849         ('|', '|https?://|')
17850         ()
17851     });
17852     
17853     /**
17854      * GFM + Line Breaks Inline Grammar
17855      */
17856     
17857     inline.breaks = merge({}, inline.gfm, {
17858       br: replace(inline.br)('{2,}', '*')(),
17859       text: replace(inline.gfm.text)('{2,}', '*')()
17860     });
17861     
17862     /**
17863      * Inline Lexer & Compiler
17864      */
17865     
17866     var InlineLexer  = function (links, options) {
17867       this.options = options || marked.defaults;
17868       this.links = links;
17869       this.rules = inline.normal;
17870       this.renderer = this.options.renderer || new Renderer;
17871       this.renderer.options = this.options;
17872     
17873       if (!this.links) {
17874         throw new
17875           Error('Tokens array requires a `links` property.');
17876       }
17877     
17878       if (this.options.gfm) {
17879         if (this.options.breaks) {
17880           this.rules = inline.breaks;
17881         } else {
17882           this.rules = inline.gfm;
17883         }
17884       } else if (this.options.pedantic) {
17885         this.rules = inline.pedantic;
17886       }
17887     }
17888     
17889     /**
17890      * Expose Inline Rules
17891      */
17892     
17893     InlineLexer.rules = inline;
17894     
17895     /**
17896      * Static Lexing/Compiling Method
17897      */
17898     
17899     InlineLexer.output = function(src, links, options) {
17900       var inline = new InlineLexer(links, options);
17901       return inline.output(src);
17902     };
17903     
17904     /**
17905      * Lexing/Compiling
17906      */
17907     
17908     InlineLexer.prototype.output = function(src) {
17909       var out = ''
17910         , link
17911         , text
17912         , href
17913         , cap;
17914     
17915       while (src) {
17916         // escape
17917         if (cap = this.rules.escape.exec(src)) {
17918           src = src.substring(cap[0].length);
17919           out += cap[1];
17920           continue;
17921         }
17922     
17923         // autolink
17924         if (cap = this.rules.autolink.exec(src)) {
17925           src = src.substring(cap[0].length);
17926           if (cap[2] === '@') {
17927             text = cap[1].charAt(6) === ':'
17928               ? this.mangle(cap[1].substring(7))
17929               : this.mangle(cap[1]);
17930             href = this.mangle('mailto:') + text;
17931           } else {
17932             text = escape(cap[1]);
17933             href = text;
17934           }
17935           out += this.renderer.link(href, null, text);
17936           continue;
17937         }
17938     
17939         // url (gfm)
17940         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17941           src = src.substring(cap[0].length);
17942           text = escape(cap[1]);
17943           href = text;
17944           out += this.renderer.link(href, null, text);
17945           continue;
17946         }
17947     
17948         // tag
17949         if (cap = this.rules.tag.exec(src)) {
17950           if (!this.inLink && /^<a /i.test(cap[0])) {
17951             this.inLink = true;
17952           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17953             this.inLink = false;
17954           }
17955           src = src.substring(cap[0].length);
17956           out += this.options.sanitize
17957             ? this.options.sanitizer
17958               ? this.options.sanitizer(cap[0])
17959               : escape(cap[0])
17960             : cap[0];
17961           continue;
17962         }
17963     
17964         // link
17965         if (cap = this.rules.link.exec(src)) {
17966           src = src.substring(cap[0].length);
17967           this.inLink = true;
17968           out += this.outputLink(cap, {
17969             href: cap[2],
17970             title: cap[3]
17971           });
17972           this.inLink = false;
17973           continue;
17974         }
17975     
17976         // reflink, nolink
17977         if ((cap = this.rules.reflink.exec(src))
17978             || (cap = this.rules.nolink.exec(src))) {
17979           src = src.substring(cap[0].length);
17980           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17981           link = this.links[link.toLowerCase()];
17982           if (!link || !link.href) {
17983             out += cap[0].charAt(0);
17984             src = cap[0].substring(1) + src;
17985             continue;
17986           }
17987           this.inLink = true;
17988           out += this.outputLink(cap, link);
17989           this.inLink = false;
17990           continue;
17991         }
17992     
17993         // strong
17994         if (cap = this.rules.strong.exec(src)) {
17995           src = src.substring(cap[0].length);
17996           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17997           continue;
17998         }
17999     
18000         // em
18001         if (cap = this.rules.em.exec(src)) {
18002           src = src.substring(cap[0].length);
18003           out += this.renderer.em(this.output(cap[2] || cap[1]));
18004           continue;
18005         }
18006     
18007         // code
18008         if (cap = this.rules.code.exec(src)) {
18009           src = src.substring(cap[0].length);
18010           out += this.renderer.codespan(escape(cap[2], true));
18011           continue;
18012         }
18013     
18014         // br
18015         if (cap = this.rules.br.exec(src)) {
18016           src = src.substring(cap[0].length);
18017           out += this.renderer.br();
18018           continue;
18019         }
18020     
18021         // del (gfm)
18022         if (cap = this.rules.del.exec(src)) {
18023           src = src.substring(cap[0].length);
18024           out += this.renderer.del(this.output(cap[1]));
18025           continue;
18026         }
18027     
18028         // text
18029         if (cap = this.rules.text.exec(src)) {
18030           src = src.substring(cap[0].length);
18031           out += this.renderer.text(escape(this.smartypants(cap[0])));
18032           continue;
18033         }
18034     
18035         if (src) {
18036           throw new
18037             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18038         }
18039       }
18040     
18041       return out;
18042     };
18043     
18044     /**
18045      * Compile Link
18046      */
18047     
18048     InlineLexer.prototype.outputLink = function(cap, link) {
18049       var href = escape(link.href)
18050         , title = link.title ? escape(link.title) : null;
18051     
18052       return cap[0].charAt(0) !== '!'
18053         ? this.renderer.link(href, title, this.output(cap[1]))
18054         : this.renderer.image(href, title, escape(cap[1]));
18055     };
18056     
18057     /**
18058      * Smartypants Transformations
18059      */
18060     
18061     InlineLexer.prototype.smartypants = function(text) {
18062       if (!this.options.smartypants)  { return text; }
18063       return text
18064         // em-dashes
18065         .replace(/---/g, '\u2014')
18066         // en-dashes
18067         .replace(/--/g, '\u2013')
18068         // opening singles
18069         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18070         // closing singles & apostrophes
18071         .replace(/'/g, '\u2019')
18072         // opening doubles
18073         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18074         // closing doubles
18075         .replace(/"/g, '\u201d')
18076         // ellipses
18077         .replace(/\.{3}/g, '\u2026');
18078     };
18079     
18080     /**
18081      * Mangle Links
18082      */
18083     
18084     InlineLexer.prototype.mangle = function(text) {
18085       if (!this.options.mangle) { return text; }
18086       var out = ''
18087         , l = text.length
18088         , i = 0
18089         , ch;
18090     
18091       for (; i < l; i++) {
18092         ch = text.charCodeAt(i);
18093         if (Math.random() > 0.5) {
18094           ch = 'x' + ch.toString(16);
18095         }
18096         out += '&#' + ch + ';';
18097       }
18098     
18099       return out;
18100     };
18101     
18102     /**
18103      * Renderer
18104      */
18105     
18106      /**
18107          * eval:var:Renderer
18108     */
18109     
18110     var Renderer   = function (options) {
18111       this.options = options || {};
18112     }
18113     
18114     Renderer.prototype.code = function(code, lang, escaped) {
18115       if (this.options.highlight) {
18116         var out = this.options.highlight(code, lang);
18117         if (out != null && out !== code) {
18118           escaped = true;
18119           code = out;
18120         }
18121       } else {
18122             // hack!!! - it's already escapeD?
18123             escaped = true;
18124       }
18125     
18126       if (!lang) {
18127         return '<pre><code>'
18128           + (escaped ? code : escape(code, true))
18129           + '\n</code></pre>';
18130       }
18131     
18132       return '<pre><code class="'
18133         + this.options.langPrefix
18134         + escape(lang, true)
18135         + '">'
18136         + (escaped ? code : escape(code, true))
18137         + '\n</code></pre>\n';
18138     };
18139     
18140     Renderer.prototype.blockquote = function(quote) {
18141       return '<blockquote>\n' + quote + '</blockquote>\n';
18142     };
18143     
18144     Renderer.prototype.html = function(html) {
18145       return html;
18146     };
18147     
18148     Renderer.prototype.heading = function(text, level, raw) {
18149       return '<h'
18150         + level
18151         + ' id="'
18152         + this.options.headerPrefix
18153         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18154         + '">'
18155         + text
18156         + '</h'
18157         + level
18158         + '>\n';
18159     };
18160     
18161     Renderer.prototype.hr = function() {
18162       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18163     };
18164     
18165     Renderer.prototype.list = function(body, ordered) {
18166       var type = ordered ? 'ol' : 'ul';
18167       return '<' + type + '>\n' + body + '</' + type + '>\n';
18168     };
18169     
18170     Renderer.prototype.listitem = function(text) {
18171       return '<li>' + text + '</li>\n';
18172     };
18173     
18174     Renderer.prototype.paragraph = function(text) {
18175       return '<p>' + text + '</p>\n';
18176     };
18177     
18178     Renderer.prototype.table = function(header, body) {
18179       return '<table class="table table-striped">\n'
18180         + '<thead>\n'
18181         + header
18182         + '</thead>\n'
18183         + '<tbody>\n'
18184         + body
18185         + '</tbody>\n'
18186         + '</table>\n';
18187     };
18188     
18189     Renderer.prototype.tablerow = function(content) {
18190       return '<tr>\n' + content + '</tr>\n';
18191     };
18192     
18193     Renderer.prototype.tablecell = function(content, flags) {
18194       var type = flags.header ? 'th' : 'td';
18195       var tag = flags.align
18196         ? '<' + type + ' style="text-align:' + flags.align + '">'
18197         : '<' + type + '>';
18198       return tag + content + '</' + type + '>\n';
18199     };
18200     
18201     // span level renderer
18202     Renderer.prototype.strong = function(text) {
18203       return '<strong>' + text + '</strong>';
18204     };
18205     
18206     Renderer.prototype.em = function(text) {
18207       return '<em>' + text + '</em>';
18208     };
18209     
18210     Renderer.prototype.codespan = function(text) {
18211       return '<code>' + text + '</code>';
18212     };
18213     
18214     Renderer.prototype.br = function() {
18215       return this.options.xhtml ? '<br/>' : '<br>';
18216     };
18217     
18218     Renderer.prototype.del = function(text) {
18219       return '<del>' + text + '</del>';
18220     };
18221     
18222     Renderer.prototype.link = function(href, title, text) {
18223       if (this.options.sanitize) {
18224         try {
18225           var prot = decodeURIComponent(unescape(href))
18226             .replace(/[^\w:]/g, '')
18227             .toLowerCase();
18228         } catch (e) {
18229           return '';
18230         }
18231         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18232           return '';
18233         }
18234       }
18235       var out = '<a href="' + href + '"';
18236       if (title) {
18237         out += ' title="' + title + '"';
18238       }
18239       out += '>' + text + '</a>';
18240       return out;
18241     };
18242     
18243     Renderer.prototype.image = function(href, title, text) {
18244       var out = '<img src="' + href + '" alt="' + text + '"';
18245       if (title) {
18246         out += ' title="' + title + '"';
18247       }
18248       out += this.options.xhtml ? '/>' : '>';
18249       return out;
18250     };
18251     
18252     Renderer.prototype.text = function(text) {
18253       return text;
18254     };
18255     
18256     /**
18257      * Parsing & Compiling
18258      */
18259          /**
18260          * eval:var:Parser
18261     */
18262     
18263     var Parser= function (options) {
18264       this.tokens = [];
18265       this.token = null;
18266       this.options = options || marked.defaults;
18267       this.options.renderer = this.options.renderer || new Renderer;
18268       this.renderer = this.options.renderer;
18269       this.renderer.options = this.options;
18270     }
18271     
18272     /**
18273      * Static Parse Method
18274      */
18275     
18276     Parser.parse = function(src, options, renderer) {
18277       var parser = new Parser(options, renderer);
18278       return parser.parse(src);
18279     };
18280     
18281     /**
18282      * Parse Loop
18283      */
18284     
18285     Parser.prototype.parse = function(src) {
18286       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18287       this.tokens = src.reverse();
18288     
18289       var out = '';
18290       while (this.next()) {
18291         out += this.tok();
18292       }
18293     
18294       return out;
18295     };
18296     
18297     /**
18298      * Next Token
18299      */
18300     
18301     Parser.prototype.next = function() {
18302       return this.token = this.tokens.pop();
18303     };
18304     
18305     /**
18306      * Preview Next Token
18307      */
18308     
18309     Parser.prototype.peek = function() {
18310       return this.tokens[this.tokens.length - 1] || 0;
18311     };
18312     
18313     /**
18314      * Parse Text Tokens
18315      */
18316     
18317     Parser.prototype.parseText = function() {
18318       var body = this.token.text;
18319     
18320       while (this.peek().type === 'text') {
18321         body += '\n' + this.next().text;
18322       }
18323     
18324       return this.inline.output(body);
18325     };
18326     
18327     /**
18328      * Parse Current Token
18329      */
18330     
18331     Parser.prototype.tok = function() {
18332       switch (this.token.type) {
18333         case 'space': {
18334           return '';
18335         }
18336         case 'hr': {
18337           return this.renderer.hr();
18338         }
18339         case 'heading': {
18340           return this.renderer.heading(
18341             this.inline.output(this.token.text),
18342             this.token.depth,
18343             this.token.text);
18344         }
18345         case 'code': {
18346           return this.renderer.code(this.token.text,
18347             this.token.lang,
18348             this.token.escaped);
18349         }
18350         case 'table': {
18351           var header = ''
18352             , body = ''
18353             , i
18354             , row
18355             , cell
18356             , flags
18357             , j;
18358     
18359           // header
18360           cell = '';
18361           for (i = 0; i < this.token.header.length; i++) {
18362             flags = { header: true, align: this.token.align[i] };
18363             cell += this.renderer.tablecell(
18364               this.inline.output(this.token.header[i]),
18365               { header: true, align: this.token.align[i] }
18366             );
18367           }
18368           header += this.renderer.tablerow(cell);
18369     
18370           for (i = 0; i < this.token.cells.length; i++) {
18371             row = this.token.cells[i];
18372     
18373             cell = '';
18374             for (j = 0; j < row.length; j++) {
18375               cell += this.renderer.tablecell(
18376                 this.inline.output(row[j]),
18377                 { header: false, align: this.token.align[j] }
18378               );
18379             }
18380     
18381             body += this.renderer.tablerow(cell);
18382           }
18383           return this.renderer.table(header, body);
18384         }
18385         case 'blockquote_start': {
18386           var body = '';
18387     
18388           while (this.next().type !== 'blockquote_end') {
18389             body += this.tok();
18390           }
18391     
18392           return this.renderer.blockquote(body);
18393         }
18394         case 'list_start': {
18395           var body = ''
18396             , ordered = this.token.ordered;
18397     
18398           while (this.next().type !== 'list_end') {
18399             body += this.tok();
18400           }
18401     
18402           return this.renderer.list(body, ordered);
18403         }
18404         case 'list_item_start': {
18405           var body = '';
18406     
18407           while (this.next().type !== 'list_item_end') {
18408             body += this.token.type === 'text'
18409               ? this.parseText()
18410               : this.tok();
18411           }
18412     
18413           return this.renderer.listitem(body);
18414         }
18415         case 'loose_item_start': {
18416           var body = '';
18417     
18418           while (this.next().type !== 'list_item_end') {
18419             body += this.tok();
18420           }
18421     
18422           return this.renderer.listitem(body);
18423         }
18424         case 'html': {
18425           var html = !this.token.pre && !this.options.pedantic
18426             ? this.inline.output(this.token.text)
18427             : this.token.text;
18428           return this.renderer.html(html);
18429         }
18430         case 'paragraph': {
18431           return this.renderer.paragraph(this.inline.output(this.token.text));
18432         }
18433         case 'text': {
18434           return this.renderer.paragraph(this.parseText());
18435         }
18436       }
18437     };
18438   
18439     
18440     /**
18441      * Marked
18442      */
18443          /**
18444          * eval:var:marked
18445     */
18446     var marked = function (src, opt, callback) {
18447       if (callback || typeof opt === 'function') {
18448         if (!callback) {
18449           callback = opt;
18450           opt = null;
18451         }
18452     
18453         opt = merge({}, marked.defaults, opt || {});
18454     
18455         var highlight = opt.highlight
18456           , tokens
18457           , pending
18458           , i = 0;
18459     
18460         try {
18461           tokens = Lexer.lex(src, opt)
18462         } catch (e) {
18463           return callback(e);
18464         }
18465     
18466         pending = tokens.length;
18467          /**
18468          * eval:var:done
18469     */
18470         var done = function(err) {
18471           if (err) {
18472             opt.highlight = highlight;
18473             return callback(err);
18474           }
18475     
18476           var out;
18477     
18478           try {
18479             out = Parser.parse(tokens, opt);
18480           } catch (e) {
18481             err = e;
18482           }
18483     
18484           opt.highlight = highlight;
18485     
18486           return err
18487             ? callback(err)
18488             : callback(null, out);
18489         };
18490     
18491         if (!highlight || highlight.length < 3) {
18492           return done();
18493         }
18494     
18495         delete opt.highlight;
18496     
18497         if (!pending) { return done(); }
18498     
18499         for (; i < tokens.length; i++) {
18500           (function(token) {
18501             if (token.type !== 'code') {
18502               return --pending || done();
18503             }
18504             return highlight(token.text, token.lang, function(err, code) {
18505               if (err) { return done(err); }
18506               if (code == null || code === token.text) {
18507                 return --pending || done();
18508               }
18509               token.text = code;
18510               token.escaped = true;
18511               --pending || done();
18512             });
18513           })(tokens[i]);
18514         }
18515     
18516         return;
18517       }
18518       try {
18519         if (opt) { opt = merge({}, marked.defaults, opt); }
18520         return Parser.parse(Lexer.lex(src, opt), opt);
18521       } catch (e) {
18522         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18523         if ((opt || marked.defaults).silent) {
18524           return '<p>An error occured:</p><pre>'
18525             + escape(e.message + '', true)
18526             + '</pre>';
18527         }
18528         throw e;
18529       }
18530     }
18531     
18532     /**
18533      * Options
18534      */
18535     
18536     marked.options =
18537     marked.setOptions = function(opt) {
18538       merge(marked.defaults, opt);
18539       return marked;
18540     };
18541     
18542     marked.defaults = {
18543       gfm: true,
18544       tables: true,
18545       breaks: false,
18546       pedantic: false,
18547       sanitize: false,
18548       sanitizer: null,
18549       mangle: true,
18550       smartLists: false,
18551       silent: false,
18552       highlight: null,
18553       langPrefix: 'lang-',
18554       smartypants: false,
18555       headerPrefix: '',
18556       renderer: new Renderer,
18557       xhtml: false
18558     };
18559     
18560     /**
18561      * Expose
18562      */
18563     
18564     marked.Parser = Parser;
18565     marked.parser = Parser.parse;
18566     
18567     marked.Renderer = Renderer;
18568     
18569     marked.Lexer = Lexer;
18570     marked.lexer = Lexer.lex;
18571     
18572     marked.InlineLexer = InlineLexer;
18573     marked.inlineLexer = InlineLexer.output;
18574     
18575     marked.parse = marked;
18576     
18577     Roo.Markdown.marked = marked;
18578
18579 })();/*
18580  * Based on:
18581  * Ext JS Library 1.1.1
18582  * Copyright(c) 2006-2007, Ext JS, LLC.
18583  *
18584  * Originally Released Under LGPL - original licence link has changed is not relivant.
18585  *
18586  * Fork - LGPL
18587  * <script type="text/javascript">
18588  */
18589
18590
18591
18592 /*
18593  * These classes are derivatives of the similarly named classes in the YUI Library.
18594  * The original license:
18595  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18596  * Code licensed under the BSD License:
18597  * http://developer.yahoo.net/yui/license.txt
18598  */
18599
18600 (function() {
18601
18602 var Event=Roo.EventManager;
18603 var Dom=Roo.lib.Dom;
18604
18605 /**
18606  * @class Roo.dd.DragDrop
18607  * @extends Roo.util.Observable
18608  * Defines the interface and base operation of items that that can be
18609  * dragged or can be drop targets.  It was designed to be extended, overriding
18610  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18611  * Up to three html elements can be associated with a DragDrop instance:
18612  * <ul>
18613  * <li>linked element: the element that is passed into the constructor.
18614  * This is the element which defines the boundaries for interaction with
18615  * other DragDrop objects.</li>
18616  * <li>handle element(s): The drag operation only occurs if the element that
18617  * was clicked matches a handle element.  By default this is the linked
18618  * element, but there are times that you will want only a portion of the
18619  * linked element to initiate the drag operation, and the setHandleElId()
18620  * method provides a way to define this.</li>
18621  * <li>drag element: this represents the element that would be moved along
18622  * with the cursor during a drag operation.  By default, this is the linked
18623  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18624  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18625  * </li>
18626  * </ul>
18627  * This class should not be instantiated until the onload event to ensure that
18628  * the associated elements are available.
18629  * The following would define a DragDrop obj that would interact with any
18630  * other DragDrop obj in the "group1" group:
18631  * <pre>
18632  *  dd = new Roo.dd.DragDrop("div1", "group1");
18633  * </pre>
18634  * Since none of the event handlers have been implemented, nothing would
18635  * actually happen if you were to run the code above.  Normally you would
18636  * override this class or one of the default implementations, but you can
18637  * also override the methods you want on an instance of the class...
18638  * <pre>
18639  *  dd.onDragDrop = function(e, id) {
18640  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18641  *  }
18642  * </pre>
18643  * @constructor
18644  * @param {String} id of the element that is linked to this instance
18645  * @param {String} sGroup the group of related DragDrop objects
18646  * @param {object} config an object containing configurable attributes
18647  *                Valid properties for DragDrop:
18648  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18649  */
18650 Roo.dd.DragDrop = function(id, sGroup, config) {
18651     if (id) {
18652         this.init(id, sGroup, config);
18653     }
18654     
18655 };
18656
18657 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18658
18659     /**
18660      * The id of the element associated with this object.  This is what we
18661      * refer to as the "linked element" because the size and position of
18662      * this element is used to determine when the drag and drop objects have
18663      * interacted.
18664      * @property id
18665      * @type String
18666      */
18667     id: null,
18668
18669     /**
18670      * Configuration attributes passed into the constructor
18671      * @property config
18672      * @type object
18673      */
18674     config: null,
18675
18676     /**
18677      * The id of the element that will be dragged.  By default this is same
18678      * as the linked element , but could be changed to another element. Ex:
18679      * Roo.dd.DDProxy
18680      * @property dragElId
18681      * @type String
18682      * @private
18683      */
18684     dragElId: null,
18685
18686     /**
18687      * the id of the element that initiates the drag operation.  By default
18688      * this is the linked element, but could be changed to be a child of this
18689      * element.  This lets us do things like only starting the drag when the
18690      * header element within the linked html element is clicked.
18691      * @property handleElId
18692      * @type String
18693      * @private
18694      */
18695     handleElId: null,
18696
18697     /**
18698      * An associative array of HTML tags that will be ignored if clicked.
18699      * @property invalidHandleTypes
18700      * @type {string: string}
18701      */
18702     invalidHandleTypes: null,
18703
18704     /**
18705      * An associative array of ids for elements that will be ignored if clicked
18706      * @property invalidHandleIds
18707      * @type {string: string}
18708      */
18709     invalidHandleIds: null,
18710
18711     /**
18712      * An indexted array of css class names for elements that will be ignored
18713      * if clicked.
18714      * @property invalidHandleClasses
18715      * @type string[]
18716      */
18717     invalidHandleClasses: null,
18718
18719     /**
18720      * The linked element's absolute X position at the time the drag was
18721      * started
18722      * @property startPageX
18723      * @type int
18724      * @private
18725      */
18726     startPageX: 0,
18727
18728     /**
18729      * The linked element's absolute X position at the time the drag was
18730      * started
18731      * @property startPageY
18732      * @type int
18733      * @private
18734      */
18735     startPageY: 0,
18736
18737     /**
18738      * The group defines a logical collection of DragDrop objects that are
18739      * related.  Instances only get events when interacting with other
18740      * DragDrop object in the same group.  This lets us define multiple
18741      * groups using a single DragDrop subclass if we want.
18742      * @property groups
18743      * @type {string: string}
18744      */
18745     groups: null,
18746
18747     /**
18748      * Individual drag/drop instances can be locked.  This will prevent
18749      * onmousedown start drag.
18750      * @property locked
18751      * @type boolean
18752      * @private
18753      */
18754     locked: false,
18755
18756     /**
18757      * Lock this instance
18758      * @method lock
18759      */
18760     lock: function() { this.locked = true; },
18761
18762     /**
18763      * Unlock this instace
18764      * @method unlock
18765      */
18766     unlock: function() { this.locked = false; },
18767
18768     /**
18769      * By default, all insances can be a drop target.  This can be disabled by
18770      * setting isTarget to false.
18771      * @method isTarget
18772      * @type boolean
18773      */
18774     isTarget: true,
18775
18776     /**
18777      * The padding configured for this drag and drop object for calculating
18778      * the drop zone intersection with this object.
18779      * @method padding
18780      * @type int[]
18781      */
18782     padding: null,
18783
18784     /**
18785      * Cached reference to the linked element
18786      * @property _domRef
18787      * @private
18788      */
18789     _domRef: null,
18790
18791     /**
18792      * Internal typeof flag
18793      * @property __ygDragDrop
18794      * @private
18795      */
18796     __ygDragDrop: true,
18797
18798     /**
18799      * Set to true when horizontal contraints are applied
18800      * @property constrainX
18801      * @type boolean
18802      * @private
18803      */
18804     constrainX: false,
18805
18806     /**
18807      * Set to true when vertical contraints are applied
18808      * @property constrainY
18809      * @type boolean
18810      * @private
18811      */
18812     constrainY: false,
18813
18814     /**
18815      * The left constraint
18816      * @property minX
18817      * @type int
18818      * @private
18819      */
18820     minX: 0,
18821
18822     /**
18823      * The right constraint
18824      * @property maxX
18825      * @type int
18826      * @private
18827      */
18828     maxX: 0,
18829
18830     /**
18831      * The up constraint
18832      * @property minY
18833      * @type int
18834      * @type int
18835      * @private
18836      */
18837     minY: 0,
18838
18839     /**
18840      * The down constraint
18841      * @property maxY
18842      * @type int
18843      * @private
18844      */
18845     maxY: 0,
18846
18847     /**
18848      * Maintain offsets when we resetconstraints.  Set to true when you want
18849      * the position of the element relative to its parent to stay the same
18850      * when the page changes
18851      *
18852      * @property maintainOffset
18853      * @type boolean
18854      */
18855     maintainOffset: false,
18856
18857     /**
18858      * Array of pixel locations the element will snap to if we specified a
18859      * horizontal graduation/interval.  This array is generated automatically
18860      * when you define a tick interval.
18861      * @property xTicks
18862      * @type int[]
18863      */
18864     xTicks: null,
18865
18866     /**
18867      * Array of pixel locations the element will snap to if we specified a
18868      * vertical graduation/interval.  This array is generated automatically
18869      * when you define a tick interval.
18870      * @property yTicks
18871      * @type int[]
18872      */
18873     yTicks: null,
18874
18875     /**
18876      * By default the drag and drop instance will only respond to the primary
18877      * button click (left button for a right-handed mouse).  Set to true to
18878      * allow drag and drop to start with any mouse click that is propogated
18879      * by the browser
18880      * @property primaryButtonOnly
18881      * @type boolean
18882      */
18883     primaryButtonOnly: true,
18884
18885     /**
18886      * The availabe property is false until the linked dom element is accessible.
18887      * @property available
18888      * @type boolean
18889      */
18890     available: false,
18891
18892     /**
18893      * By default, drags can only be initiated if the mousedown occurs in the
18894      * region the linked element is.  This is done in part to work around a
18895      * bug in some browsers that mis-report the mousedown if the previous
18896      * mouseup happened outside of the window.  This property is set to true
18897      * if outer handles are defined.
18898      *
18899      * @property hasOuterHandles
18900      * @type boolean
18901      * @default false
18902      */
18903     hasOuterHandles: false,
18904
18905     /**
18906      * Code that executes immediately before the startDrag event
18907      * @method b4StartDrag
18908      * @private
18909      */
18910     b4StartDrag: function(x, y) { },
18911
18912     /**
18913      * Abstract method called after a drag/drop object is clicked
18914      * and the drag or mousedown time thresholds have beeen met.
18915      * @method startDrag
18916      * @param {int} X click location
18917      * @param {int} Y click location
18918      */
18919     startDrag: function(x, y) { /* override this */ },
18920
18921     /**
18922      * Code that executes immediately before the onDrag event
18923      * @method b4Drag
18924      * @private
18925      */
18926     b4Drag: function(e) { },
18927
18928     /**
18929      * Abstract method called during the onMouseMove event while dragging an
18930      * object.
18931      * @method onDrag
18932      * @param {Event} e the mousemove event
18933      */
18934     onDrag: function(e) { /* override this */ },
18935
18936     /**
18937      * Abstract method called when this element fist begins hovering over
18938      * another DragDrop obj
18939      * @method onDragEnter
18940      * @param {Event} e the mousemove event
18941      * @param {String|DragDrop[]} id In POINT mode, the element
18942      * id this is hovering over.  In INTERSECT mode, an array of one or more
18943      * dragdrop items being hovered over.
18944      */
18945     onDragEnter: function(e, id) { /* override this */ },
18946
18947     /**
18948      * Code that executes immediately before the onDragOver event
18949      * @method b4DragOver
18950      * @private
18951      */
18952     b4DragOver: function(e) { },
18953
18954     /**
18955      * Abstract method called when this element is hovering over another
18956      * DragDrop obj
18957      * @method onDragOver
18958      * @param {Event} e the mousemove event
18959      * @param {String|DragDrop[]} id In POINT mode, the element
18960      * id this is hovering over.  In INTERSECT mode, an array of dd items
18961      * being hovered over.
18962      */
18963     onDragOver: function(e, id) { /* override this */ },
18964
18965     /**
18966      * Code that executes immediately before the onDragOut event
18967      * @method b4DragOut
18968      * @private
18969      */
18970     b4DragOut: function(e) { },
18971
18972     /**
18973      * Abstract method called when we are no longer hovering over an element
18974      * @method onDragOut
18975      * @param {Event} e the mousemove event
18976      * @param {String|DragDrop[]} id In POINT mode, the element
18977      * id this was hovering over.  In INTERSECT mode, an array of dd items
18978      * that the mouse is no longer over.
18979      */
18980     onDragOut: function(e, id) { /* override this */ },
18981
18982     /**
18983      * Code that executes immediately before the onDragDrop event
18984      * @method b4DragDrop
18985      * @private
18986      */
18987     b4DragDrop: function(e) { },
18988
18989     /**
18990      * Abstract method called when this item is dropped on another DragDrop
18991      * obj
18992      * @method onDragDrop
18993      * @param {Event} e the mouseup event
18994      * @param {String|DragDrop[]} id In POINT mode, the element
18995      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18996      * was dropped on.
18997      */
18998     onDragDrop: function(e, id) { /* override this */ },
18999
19000     /**
19001      * Abstract method called when this item is dropped on an area with no
19002      * drop target
19003      * @method onInvalidDrop
19004      * @param {Event} e the mouseup event
19005      */
19006     onInvalidDrop: function(e) { /* override this */ },
19007
19008     /**
19009      * Code that executes immediately before the endDrag event
19010      * @method b4EndDrag
19011      * @private
19012      */
19013     b4EndDrag: function(e) { },
19014
19015     /**
19016      * Fired when we are done dragging the object
19017      * @method endDrag
19018      * @param {Event} e the mouseup event
19019      */
19020     endDrag: function(e) { /* override this */ },
19021
19022     /**
19023      * Code executed immediately before the onMouseDown event
19024      * @method b4MouseDown
19025      * @param {Event} e the mousedown event
19026      * @private
19027      */
19028     b4MouseDown: function(e) {  },
19029
19030     /**
19031      * Event handler that fires when a drag/drop obj gets a mousedown
19032      * @method onMouseDown
19033      * @param {Event} e the mousedown event
19034      */
19035     onMouseDown: function(e) { /* override this */ },
19036
19037     /**
19038      * Event handler that fires when a drag/drop obj gets a mouseup
19039      * @method onMouseUp
19040      * @param {Event} e the mouseup event
19041      */
19042     onMouseUp: function(e) { /* override this */ },
19043
19044     /**
19045      * Override the onAvailable method to do what is needed after the initial
19046      * position was determined.
19047      * @method onAvailable
19048      */
19049     onAvailable: function () {
19050     },
19051
19052     /*
19053      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19054      * @type Object
19055      */
19056     defaultPadding : {left:0, right:0, top:0, bottom:0},
19057
19058     /*
19059      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19060  *
19061  * Usage:
19062  <pre><code>
19063  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19064                 { dragElId: "existingProxyDiv" });
19065  dd.startDrag = function(){
19066      this.constrainTo("parent-id");
19067  };
19068  </code></pre>
19069  * Or you can initalize it using the {@link Roo.Element} object:
19070  <pre><code>
19071  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19072      startDrag : function(){
19073          this.constrainTo("parent-id");
19074      }
19075  });
19076  </code></pre>
19077      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19078      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19079      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19080      * an object containing the sides to pad. For example: {right:10, bottom:10}
19081      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19082      */
19083     constrainTo : function(constrainTo, pad, inContent){
19084         if(typeof pad == "number"){
19085             pad = {left: pad, right:pad, top:pad, bottom:pad};
19086         }
19087         pad = pad || this.defaultPadding;
19088         var b = Roo.get(this.getEl()).getBox();
19089         var ce = Roo.get(constrainTo);
19090         var s = ce.getScroll();
19091         var c, cd = ce.dom;
19092         if(cd == document.body){
19093             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19094         }else{
19095             xy = ce.getXY();
19096             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19097         }
19098
19099
19100         var topSpace = b.y - c.y;
19101         var leftSpace = b.x - c.x;
19102
19103         this.resetConstraints();
19104         this.setXConstraint(leftSpace - (pad.left||0), // left
19105                 c.width - leftSpace - b.width - (pad.right||0) //right
19106         );
19107         this.setYConstraint(topSpace - (pad.top||0), //top
19108                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19109         );
19110     },
19111
19112     /**
19113      * Returns a reference to the linked element
19114      * @method getEl
19115      * @return {HTMLElement} the html element
19116      */
19117     getEl: function() {
19118         if (!this._domRef) {
19119             this._domRef = Roo.getDom(this.id);
19120         }
19121
19122         return this._domRef;
19123     },
19124
19125     /**
19126      * Returns a reference to the actual element to drag.  By default this is
19127      * the same as the html element, but it can be assigned to another
19128      * element. An example of this can be found in Roo.dd.DDProxy
19129      * @method getDragEl
19130      * @return {HTMLElement} the html element
19131      */
19132     getDragEl: function() {
19133         return Roo.getDom(this.dragElId);
19134     },
19135
19136     /**
19137      * Sets up the DragDrop object.  Must be called in the constructor of any
19138      * Roo.dd.DragDrop subclass
19139      * @method init
19140      * @param id the id of the linked element
19141      * @param {String} sGroup the group of related items
19142      * @param {object} config configuration attributes
19143      */
19144     init: function(id, sGroup, config) {
19145         this.initTarget(id, sGroup, config);
19146         if (!Roo.isTouch) {
19147             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19148         }
19149         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19150         // Event.on(this.id, "selectstart", Event.preventDefault);
19151     },
19152
19153     /**
19154      * Initializes Targeting functionality only... the object does not
19155      * get a mousedown handler.
19156      * @method initTarget
19157      * @param id the id of the linked element
19158      * @param {String} sGroup the group of related items
19159      * @param {object} config configuration attributes
19160      */
19161     initTarget: function(id, sGroup, config) {
19162
19163         // configuration attributes
19164         this.config = config || {};
19165
19166         // create a local reference to the drag and drop manager
19167         this.DDM = Roo.dd.DDM;
19168         // initialize the groups array
19169         this.groups = {};
19170
19171         // assume that we have an element reference instead of an id if the
19172         // parameter is not a string
19173         if (typeof id !== "string") {
19174             id = Roo.id(id);
19175         }
19176
19177         // set the id
19178         this.id = id;
19179
19180         // add to an interaction group
19181         this.addToGroup((sGroup) ? sGroup : "default");
19182
19183         // We don't want to register this as the handle with the manager
19184         // so we just set the id rather than calling the setter.
19185         this.handleElId = id;
19186
19187         // the linked element is the element that gets dragged by default
19188         this.setDragElId(id);
19189
19190         // by default, clicked anchors will not start drag operations.
19191         this.invalidHandleTypes = { A: "A" };
19192         this.invalidHandleIds = {};
19193         this.invalidHandleClasses = [];
19194
19195         this.applyConfig();
19196
19197         this.handleOnAvailable();
19198     },
19199
19200     /**
19201      * Applies the configuration parameters that were passed into the constructor.
19202      * This is supposed to happen at each level through the inheritance chain.  So
19203      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19204      * DragDrop in order to get all of the parameters that are available in
19205      * each object.
19206      * @method applyConfig
19207      */
19208     applyConfig: function() {
19209
19210         // configurable properties:
19211         //    padding, isTarget, maintainOffset, primaryButtonOnly
19212         this.padding           = this.config.padding || [0, 0, 0, 0];
19213         this.isTarget          = (this.config.isTarget !== false);
19214         this.maintainOffset    = (this.config.maintainOffset);
19215         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19216
19217     },
19218
19219     /**
19220      * Executed when the linked element is available
19221      * @method handleOnAvailable
19222      * @private
19223      */
19224     handleOnAvailable: function() {
19225         this.available = true;
19226         this.resetConstraints();
19227         this.onAvailable();
19228     },
19229
19230      /**
19231      * Configures the padding for the target zone in px.  Effectively expands
19232      * (or reduces) the virtual object size for targeting calculations.
19233      * Supports css-style shorthand; if only one parameter is passed, all sides
19234      * will have that padding, and if only two are passed, the top and bottom
19235      * will have the first param, the left and right the second.
19236      * @method setPadding
19237      * @param {int} iTop    Top pad
19238      * @param {int} iRight  Right pad
19239      * @param {int} iBot    Bot pad
19240      * @param {int} iLeft   Left pad
19241      */
19242     setPadding: function(iTop, iRight, iBot, iLeft) {
19243         // this.padding = [iLeft, iRight, iTop, iBot];
19244         if (!iRight && 0 !== iRight) {
19245             this.padding = [iTop, iTop, iTop, iTop];
19246         } else if (!iBot && 0 !== iBot) {
19247             this.padding = [iTop, iRight, iTop, iRight];
19248         } else {
19249             this.padding = [iTop, iRight, iBot, iLeft];
19250         }
19251     },
19252
19253     /**
19254      * Stores the initial placement of the linked element.
19255      * @method setInitialPosition
19256      * @param {int} diffX   the X offset, default 0
19257      * @param {int} diffY   the Y offset, default 0
19258      */
19259     setInitPosition: function(diffX, diffY) {
19260         var el = this.getEl();
19261
19262         if (!this.DDM.verifyEl(el)) {
19263             return;
19264         }
19265
19266         var dx = diffX || 0;
19267         var dy = diffY || 0;
19268
19269         var p = Dom.getXY( el );
19270
19271         this.initPageX = p[0] - dx;
19272         this.initPageY = p[1] - dy;
19273
19274         this.lastPageX = p[0];
19275         this.lastPageY = p[1];
19276
19277
19278         this.setStartPosition(p);
19279     },
19280
19281     /**
19282      * Sets the start position of the element.  This is set when the obj
19283      * is initialized, the reset when a drag is started.
19284      * @method setStartPosition
19285      * @param pos current position (from previous lookup)
19286      * @private
19287      */
19288     setStartPosition: function(pos) {
19289         var p = pos || Dom.getXY( this.getEl() );
19290         this.deltaSetXY = null;
19291
19292         this.startPageX = p[0];
19293         this.startPageY = p[1];
19294     },
19295
19296     /**
19297      * Add this instance to a group of related drag/drop objects.  All
19298      * instances belong to at least one group, and can belong to as many
19299      * groups as needed.
19300      * @method addToGroup
19301      * @param sGroup {string} the name of the group
19302      */
19303     addToGroup: function(sGroup) {
19304         this.groups[sGroup] = true;
19305         this.DDM.regDragDrop(this, sGroup);
19306     },
19307
19308     /**
19309      * Remove's this instance from the supplied interaction group
19310      * @method removeFromGroup
19311      * @param {string}  sGroup  The group to drop
19312      */
19313     removeFromGroup: function(sGroup) {
19314         if (this.groups[sGroup]) {
19315             delete this.groups[sGroup];
19316         }
19317
19318         this.DDM.removeDDFromGroup(this, sGroup);
19319     },
19320
19321     /**
19322      * Allows you to specify that an element other than the linked element
19323      * will be moved with the cursor during a drag
19324      * @method setDragElId
19325      * @param id {string} the id of the element that will be used to initiate the drag
19326      */
19327     setDragElId: function(id) {
19328         this.dragElId = id;
19329     },
19330
19331     /**
19332      * Allows you to specify a child of the linked element that should be
19333      * used to initiate the drag operation.  An example of this would be if
19334      * you have a content div with text and links.  Clicking anywhere in the
19335      * content area would normally start the drag operation.  Use this method
19336      * to specify that an element inside of the content div is the element
19337      * that starts the drag operation.
19338      * @method setHandleElId
19339      * @param id {string} the id of the element that will be used to
19340      * initiate the drag.
19341      */
19342     setHandleElId: function(id) {
19343         if (typeof id !== "string") {
19344             id = Roo.id(id);
19345         }
19346         this.handleElId = id;
19347         this.DDM.regHandle(this.id, id);
19348     },
19349
19350     /**
19351      * Allows you to set an element outside of the linked element as a drag
19352      * handle
19353      * @method setOuterHandleElId
19354      * @param id the id of the element that will be used to initiate the drag
19355      */
19356     setOuterHandleElId: function(id) {
19357         if (typeof id !== "string") {
19358             id = Roo.id(id);
19359         }
19360         Event.on(id, "mousedown",
19361                 this.handleMouseDown, this);
19362         this.setHandleElId(id);
19363
19364         this.hasOuterHandles = true;
19365     },
19366
19367     /**
19368      * Remove all drag and drop hooks for this element
19369      * @method unreg
19370      */
19371     unreg: function() {
19372         Event.un(this.id, "mousedown",
19373                 this.handleMouseDown);
19374         Event.un(this.id, "touchstart",
19375                 this.handleMouseDown);
19376         this._domRef = null;
19377         this.DDM._remove(this);
19378     },
19379
19380     destroy : function(){
19381         this.unreg();
19382     },
19383
19384     /**
19385      * Returns true if this instance is locked, or the drag drop mgr is locked
19386      * (meaning that all drag/drop is disabled on the page.)
19387      * @method isLocked
19388      * @return {boolean} true if this obj or all drag/drop is locked, else
19389      * false
19390      */
19391     isLocked: function() {
19392         return (this.DDM.isLocked() || this.locked);
19393     },
19394
19395     /**
19396      * Fired when this object is clicked
19397      * @method handleMouseDown
19398      * @param {Event} e
19399      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19400      * @private
19401      */
19402     handleMouseDown: function(e, oDD){
19403      
19404         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19405             //Roo.log('not touch/ button !=0');
19406             return;
19407         }
19408         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19409             return; // double touch..
19410         }
19411         
19412
19413         if (this.isLocked()) {
19414             //Roo.log('locked');
19415             return;
19416         }
19417
19418         this.DDM.refreshCache(this.groups);
19419 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19420         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19421         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19422             //Roo.log('no outer handes or not over target');
19423                 // do nothing.
19424         } else {
19425 //            Roo.log('check validator');
19426             if (this.clickValidator(e)) {
19427 //                Roo.log('validate success');
19428                 // set the initial element position
19429                 this.setStartPosition();
19430
19431
19432                 this.b4MouseDown(e);
19433                 this.onMouseDown(e);
19434
19435                 this.DDM.handleMouseDown(e, this);
19436
19437                 this.DDM.stopEvent(e);
19438             } else {
19439
19440
19441             }
19442         }
19443     },
19444
19445     clickValidator: function(e) {
19446         var target = e.getTarget();
19447         return ( this.isValidHandleChild(target) &&
19448                     (this.id == this.handleElId ||
19449                         this.DDM.handleWasClicked(target, this.id)) );
19450     },
19451
19452     /**
19453      * Allows you to specify a tag name that should not start a drag operation
19454      * when clicked.  This is designed to facilitate embedding links within a
19455      * drag handle that do something other than start the drag.
19456      * @method addInvalidHandleType
19457      * @param {string} tagName the type of element to exclude
19458      */
19459     addInvalidHandleType: function(tagName) {
19460         var type = tagName.toUpperCase();
19461         this.invalidHandleTypes[type] = type;
19462     },
19463
19464     /**
19465      * Lets you to specify an element id for a child of a drag handle
19466      * that should not initiate a drag
19467      * @method addInvalidHandleId
19468      * @param {string} id the element id of the element you wish to ignore
19469      */
19470     addInvalidHandleId: function(id) {
19471         if (typeof id !== "string") {
19472             id = Roo.id(id);
19473         }
19474         this.invalidHandleIds[id] = id;
19475     },
19476
19477     /**
19478      * Lets you specify a css class of elements that will not initiate a drag
19479      * @method addInvalidHandleClass
19480      * @param {string} cssClass the class of the elements you wish to ignore
19481      */
19482     addInvalidHandleClass: function(cssClass) {
19483         this.invalidHandleClasses.push(cssClass);
19484     },
19485
19486     /**
19487      * Unsets an excluded tag name set by addInvalidHandleType
19488      * @method removeInvalidHandleType
19489      * @param {string} tagName the type of element to unexclude
19490      */
19491     removeInvalidHandleType: function(tagName) {
19492         var type = tagName.toUpperCase();
19493         // this.invalidHandleTypes[type] = null;
19494         delete this.invalidHandleTypes[type];
19495     },
19496
19497     /**
19498      * Unsets an invalid handle id
19499      * @method removeInvalidHandleId
19500      * @param {string} id the id of the element to re-enable
19501      */
19502     removeInvalidHandleId: function(id) {
19503         if (typeof id !== "string") {
19504             id = Roo.id(id);
19505         }
19506         delete this.invalidHandleIds[id];
19507     },
19508
19509     /**
19510      * Unsets an invalid css class
19511      * @method removeInvalidHandleClass
19512      * @param {string} cssClass the class of the element(s) you wish to
19513      * re-enable
19514      */
19515     removeInvalidHandleClass: function(cssClass) {
19516         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19517             if (this.invalidHandleClasses[i] == cssClass) {
19518                 delete this.invalidHandleClasses[i];
19519             }
19520         }
19521     },
19522
19523     /**
19524      * Checks the tag exclusion list to see if this click should be ignored
19525      * @method isValidHandleChild
19526      * @param {HTMLElement} node the HTMLElement to evaluate
19527      * @return {boolean} true if this is a valid tag type, false if not
19528      */
19529     isValidHandleChild: function(node) {
19530
19531         var valid = true;
19532         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19533         var nodeName;
19534         try {
19535             nodeName = node.nodeName.toUpperCase();
19536         } catch(e) {
19537             nodeName = node.nodeName;
19538         }
19539         valid = valid && !this.invalidHandleTypes[nodeName];
19540         valid = valid && !this.invalidHandleIds[node.id];
19541
19542         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19543             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19544         }
19545
19546
19547         return valid;
19548
19549     },
19550
19551     /**
19552      * Create the array of horizontal tick marks if an interval was specified
19553      * in setXConstraint().
19554      * @method setXTicks
19555      * @private
19556      */
19557     setXTicks: function(iStartX, iTickSize) {
19558         this.xTicks = [];
19559         this.xTickSize = iTickSize;
19560
19561         var tickMap = {};
19562
19563         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19564             if (!tickMap[i]) {
19565                 this.xTicks[this.xTicks.length] = i;
19566                 tickMap[i] = true;
19567             }
19568         }
19569
19570         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19571             if (!tickMap[i]) {
19572                 this.xTicks[this.xTicks.length] = i;
19573                 tickMap[i] = true;
19574             }
19575         }
19576
19577         this.xTicks.sort(this.DDM.numericSort) ;
19578     },
19579
19580     /**
19581      * Create the array of vertical tick marks if an interval was specified in
19582      * setYConstraint().
19583      * @method setYTicks
19584      * @private
19585      */
19586     setYTicks: function(iStartY, iTickSize) {
19587         this.yTicks = [];
19588         this.yTickSize = iTickSize;
19589
19590         var tickMap = {};
19591
19592         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19593             if (!tickMap[i]) {
19594                 this.yTicks[this.yTicks.length] = i;
19595                 tickMap[i] = true;
19596             }
19597         }
19598
19599         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19600             if (!tickMap[i]) {
19601                 this.yTicks[this.yTicks.length] = i;
19602                 tickMap[i] = true;
19603             }
19604         }
19605
19606         this.yTicks.sort(this.DDM.numericSort) ;
19607     },
19608
19609     /**
19610      * By default, the element can be dragged any place on the screen.  Use
19611      * this method to limit the horizontal travel of the element.  Pass in
19612      * 0,0 for the parameters if you want to lock the drag to the y axis.
19613      * @method setXConstraint
19614      * @param {int} iLeft the number of pixels the element can move to the left
19615      * @param {int} iRight the number of pixels the element can move to the
19616      * right
19617      * @param {int} iTickSize optional parameter for specifying that the
19618      * element
19619      * should move iTickSize pixels at a time.
19620      */
19621     setXConstraint: function(iLeft, iRight, iTickSize) {
19622         this.leftConstraint = iLeft;
19623         this.rightConstraint = iRight;
19624
19625         this.minX = this.initPageX - iLeft;
19626         this.maxX = this.initPageX + iRight;
19627         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19628
19629         this.constrainX = true;
19630     },
19631
19632     /**
19633      * Clears any constraints applied to this instance.  Also clears ticks
19634      * since they can't exist independent of a constraint at this time.
19635      * @method clearConstraints
19636      */
19637     clearConstraints: function() {
19638         this.constrainX = false;
19639         this.constrainY = false;
19640         this.clearTicks();
19641     },
19642
19643     /**
19644      * Clears any tick interval defined for this instance
19645      * @method clearTicks
19646      */
19647     clearTicks: function() {
19648         this.xTicks = null;
19649         this.yTicks = null;
19650         this.xTickSize = 0;
19651         this.yTickSize = 0;
19652     },
19653
19654     /**
19655      * By default, the element can be dragged any place on the screen.  Set
19656      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19657      * parameters if you want to lock the drag to the x axis.
19658      * @method setYConstraint
19659      * @param {int} iUp the number of pixels the element can move up
19660      * @param {int} iDown the number of pixels the element can move down
19661      * @param {int} iTickSize optional parameter for specifying that the
19662      * element should move iTickSize pixels at a time.
19663      */
19664     setYConstraint: function(iUp, iDown, iTickSize) {
19665         this.topConstraint = iUp;
19666         this.bottomConstraint = iDown;
19667
19668         this.minY = this.initPageY - iUp;
19669         this.maxY = this.initPageY + iDown;
19670         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19671
19672         this.constrainY = true;
19673
19674     },
19675
19676     /**
19677      * resetConstraints must be called if you manually reposition a dd element.
19678      * @method resetConstraints
19679      * @param {boolean} maintainOffset
19680      */
19681     resetConstraints: function() {
19682
19683
19684         // Maintain offsets if necessary
19685         if (this.initPageX || this.initPageX === 0) {
19686             // figure out how much this thing has moved
19687             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19688             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19689
19690             this.setInitPosition(dx, dy);
19691
19692         // This is the first time we have detected the element's position
19693         } else {
19694             this.setInitPosition();
19695         }
19696
19697         if (this.constrainX) {
19698             this.setXConstraint( this.leftConstraint,
19699                                  this.rightConstraint,
19700                                  this.xTickSize        );
19701         }
19702
19703         if (this.constrainY) {
19704             this.setYConstraint( this.topConstraint,
19705                                  this.bottomConstraint,
19706                                  this.yTickSize         );
19707         }
19708     },
19709
19710     /**
19711      * Normally the drag element is moved pixel by pixel, but we can specify
19712      * that it move a number of pixels at a time.  This method resolves the
19713      * location when we have it set up like this.
19714      * @method getTick
19715      * @param {int} val where we want to place the object
19716      * @param {int[]} tickArray sorted array of valid points
19717      * @return {int} the closest tick
19718      * @private
19719      */
19720     getTick: function(val, tickArray) {
19721
19722         if (!tickArray) {
19723             // If tick interval is not defined, it is effectively 1 pixel,
19724             // so we return the value passed to us.
19725             return val;
19726         } else if (tickArray[0] >= val) {
19727             // The value is lower than the first tick, so we return the first
19728             // tick.
19729             return tickArray[0];
19730         } else {
19731             for (var i=0, len=tickArray.length; i<len; ++i) {
19732                 var next = i + 1;
19733                 if (tickArray[next] && tickArray[next] >= val) {
19734                     var diff1 = val - tickArray[i];
19735                     var diff2 = tickArray[next] - val;
19736                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19737                 }
19738             }
19739
19740             // The value is larger than the last tick, so we return the last
19741             // tick.
19742             return tickArray[tickArray.length - 1];
19743         }
19744     },
19745
19746     /**
19747      * toString method
19748      * @method toString
19749      * @return {string} string representation of the dd obj
19750      */
19751     toString: function() {
19752         return ("DragDrop " + this.id);
19753     }
19754
19755 });
19756
19757 })();
19758 /*
19759  * Based on:
19760  * Ext JS Library 1.1.1
19761  * Copyright(c) 2006-2007, Ext JS, LLC.
19762  *
19763  * Originally Released Under LGPL - original licence link has changed is not relivant.
19764  *
19765  * Fork - LGPL
19766  * <script type="text/javascript">
19767  */
19768
19769
19770 /**
19771  * The drag and drop utility provides a framework for building drag and drop
19772  * applications.  In addition to enabling drag and drop for specific elements,
19773  * the drag and drop elements are tracked by the manager class, and the
19774  * interactions between the various elements are tracked during the drag and
19775  * the implementing code is notified about these important moments.
19776  */
19777
19778 // Only load the library once.  Rewriting the manager class would orphan
19779 // existing drag and drop instances.
19780 if (!Roo.dd.DragDropMgr) {
19781
19782 /**
19783  * @class Roo.dd.DragDropMgr
19784  * DragDropMgr is a singleton that tracks the element interaction for
19785  * all DragDrop items in the window.  Generally, you will not call
19786  * this class directly, but it does have helper methods that could
19787  * be useful in your DragDrop implementations.
19788  * @singleton
19789  */
19790 Roo.dd.DragDropMgr = function() {
19791
19792     var Event = Roo.EventManager;
19793
19794     return {
19795
19796         /**
19797          * Two dimensional Array of registered DragDrop objects.  The first
19798          * dimension is the DragDrop item group, the second the DragDrop
19799          * object.
19800          * @property ids
19801          * @type {string: string}
19802          * @private
19803          * @static
19804          */
19805         ids: {},
19806
19807         /**
19808          * Array of element ids defined as drag handles.  Used to determine
19809          * if the element that generated the mousedown event is actually the
19810          * handle and not the html element itself.
19811          * @property handleIds
19812          * @type {string: string}
19813          * @private
19814          * @static
19815          */
19816         handleIds: {},
19817
19818         /**
19819          * the DragDrop object that is currently being dragged
19820          * @property dragCurrent
19821          * @type DragDrop
19822          * @private
19823          * @static
19824          **/
19825         dragCurrent: null,
19826
19827         /**
19828          * the DragDrop object(s) that are being hovered over
19829          * @property dragOvers
19830          * @type Array
19831          * @private
19832          * @static
19833          */
19834         dragOvers: {},
19835
19836         /**
19837          * the X distance between the cursor and the object being dragged
19838          * @property deltaX
19839          * @type int
19840          * @private
19841          * @static
19842          */
19843         deltaX: 0,
19844
19845         /**
19846          * the Y distance between the cursor and the object being dragged
19847          * @property deltaY
19848          * @type int
19849          * @private
19850          * @static
19851          */
19852         deltaY: 0,
19853
19854         /**
19855          * Flag to determine if we should prevent the default behavior of the
19856          * events we define. By default this is true, but this can be set to
19857          * false if you need the default behavior (not recommended)
19858          * @property preventDefault
19859          * @type boolean
19860          * @static
19861          */
19862         preventDefault: true,
19863
19864         /**
19865          * Flag to determine if we should stop the propagation of the events
19866          * we generate. This is true by default but you may want to set it to
19867          * false if the html element contains other features that require the
19868          * mouse click.
19869          * @property stopPropagation
19870          * @type boolean
19871          * @static
19872          */
19873         stopPropagation: true,
19874
19875         /**
19876          * Internal flag that is set to true when drag and drop has been
19877          * intialized
19878          * @property initialized
19879          * @private
19880          * @static
19881          */
19882         initalized: false,
19883
19884         /**
19885          * All drag and drop can be disabled.
19886          * @property locked
19887          * @private
19888          * @static
19889          */
19890         locked: false,
19891
19892         /**
19893          * Called the first time an element is registered.
19894          * @method init
19895          * @private
19896          * @static
19897          */
19898         init: function() {
19899             this.initialized = true;
19900         },
19901
19902         /**
19903          * In point mode, drag and drop interaction is defined by the
19904          * location of the cursor during the drag/drop
19905          * @property POINT
19906          * @type int
19907          * @static
19908          */
19909         POINT: 0,
19910
19911         /**
19912          * In intersect mode, drag and drop interactio nis defined by the
19913          * overlap of two or more drag and drop objects.
19914          * @property INTERSECT
19915          * @type int
19916          * @static
19917          */
19918         INTERSECT: 1,
19919
19920         /**
19921          * The current drag and drop mode.  Default: POINT
19922          * @property mode
19923          * @type int
19924          * @static
19925          */
19926         mode: 0,
19927
19928         /**
19929          * Runs method on all drag and drop objects
19930          * @method _execOnAll
19931          * @private
19932          * @static
19933          */
19934         _execOnAll: function(sMethod, args) {
19935             for (var i in this.ids) {
19936                 for (var j in this.ids[i]) {
19937                     var oDD = this.ids[i][j];
19938                     if (! this.isTypeOfDD(oDD)) {
19939                         continue;
19940                     }
19941                     oDD[sMethod].apply(oDD, args);
19942                 }
19943             }
19944         },
19945
19946         /**
19947          * Drag and drop initialization.  Sets up the global event handlers
19948          * @method _onLoad
19949          * @private
19950          * @static
19951          */
19952         _onLoad: function() {
19953
19954             this.init();
19955
19956             if (!Roo.isTouch) {
19957                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19958                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19959             }
19960             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19961             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19962             
19963             Event.on(window,   "unload",    this._onUnload, this, true);
19964             Event.on(window,   "resize",    this._onResize, this, true);
19965             // Event.on(window,   "mouseout",    this._test);
19966
19967         },
19968
19969         /**
19970          * Reset constraints on all drag and drop objs
19971          * @method _onResize
19972          * @private
19973          * @static
19974          */
19975         _onResize: function(e) {
19976             this._execOnAll("resetConstraints", []);
19977         },
19978
19979         /**
19980          * Lock all drag and drop functionality
19981          * @method lock
19982          * @static
19983          */
19984         lock: function() { this.locked = true; },
19985
19986         /**
19987          * Unlock all drag and drop functionality
19988          * @method unlock
19989          * @static
19990          */
19991         unlock: function() { this.locked = false; },
19992
19993         /**
19994          * Is drag and drop locked?
19995          * @method isLocked
19996          * @return {boolean} True if drag and drop is locked, false otherwise.
19997          * @static
19998          */
19999         isLocked: function() { return this.locked; },
20000
20001         /**
20002          * Location cache that is set for all drag drop objects when a drag is
20003          * initiated, cleared when the drag is finished.
20004          * @property locationCache
20005          * @private
20006          * @static
20007          */
20008         locationCache: {},
20009
20010         /**
20011          * Set useCache to false if you want to force object the lookup of each
20012          * drag and drop linked element constantly during a drag.
20013          * @property useCache
20014          * @type boolean
20015          * @static
20016          */
20017         useCache: true,
20018
20019         /**
20020          * The number of pixels that the mouse needs to move after the
20021          * mousedown before the drag is initiated.  Default=3;
20022          * @property clickPixelThresh
20023          * @type int
20024          * @static
20025          */
20026         clickPixelThresh: 3,
20027
20028         /**
20029          * The number of milliseconds after the mousedown event to initiate the
20030          * drag if we don't get a mouseup event. Default=1000
20031          * @property clickTimeThresh
20032          * @type int
20033          * @static
20034          */
20035         clickTimeThresh: 350,
20036
20037         /**
20038          * Flag that indicates that either the drag pixel threshold or the
20039          * mousdown time threshold has been met
20040          * @property dragThreshMet
20041          * @type boolean
20042          * @private
20043          * @static
20044          */
20045         dragThreshMet: false,
20046
20047         /**
20048          * Timeout used for the click time threshold
20049          * @property clickTimeout
20050          * @type Object
20051          * @private
20052          * @static
20053          */
20054         clickTimeout: null,
20055
20056         /**
20057          * The X position of the mousedown event stored for later use when a
20058          * drag threshold is met.
20059          * @property startX
20060          * @type int
20061          * @private
20062          * @static
20063          */
20064         startX: 0,
20065
20066         /**
20067          * The Y position of the mousedown event stored for later use when a
20068          * drag threshold is met.
20069          * @property startY
20070          * @type int
20071          * @private
20072          * @static
20073          */
20074         startY: 0,
20075
20076         /**
20077          * Each DragDrop instance must be registered with the DragDropMgr.
20078          * This is executed in DragDrop.init()
20079          * @method regDragDrop
20080          * @param {DragDrop} oDD the DragDrop object to register
20081          * @param {String} sGroup the name of the group this element belongs to
20082          * @static
20083          */
20084         regDragDrop: function(oDD, sGroup) {
20085             if (!this.initialized) { this.init(); }
20086
20087             if (!this.ids[sGroup]) {
20088                 this.ids[sGroup] = {};
20089             }
20090             this.ids[sGroup][oDD.id] = oDD;
20091         },
20092
20093         /**
20094          * Removes the supplied dd instance from the supplied group. Executed
20095          * by DragDrop.removeFromGroup, so don't call this function directly.
20096          * @method removeDDFromGroup
20097          * @private
20098          * @static
20099          */
20100         removeDDFromGroup: function(oDD, sGroup) {
20101             if (!this.ids[sGroup]) {
20102                 this.ids[sGroup] = {};
20103             }
20104
20105             var obj = this.ids[sGroup];
20106             if (obj && obj[oDD.id]) {
20107                 delete obj[oDD.id];
20108             }
20109         },
20110
20111         /**
20112          * Unregisters a drag and drop item.  This is executed in
20113          * DragDrop.unreg, use that method instead of calling this directly.
20114          * @method _remove
20115          * @private
20116          * @static
20117          */
20118         _remove: function(oDD) {
20119             for (var g in oDD.groups) {
20120                 if (g && this.ids[g][oDD.id]) {
20121                     delete this.ids[g][oDD.id];
20122                 }
20123             }
20124             delete this.handleIds[oDD.id];
20125         },
20126
20127         /**
20128          * Each DragDrop handle element must be registered.  This is done
20129          * automatically when executing DragDrop.setHandleElId()
20130          * @method regHandle
20131          * @param {String} sDDId the DragDrop id this element is a handle for
20132          * @param {String} sHandleId the id of the element that is the drag
20133          * handle
20134          * @static
20135          */
20136         regHandle: function(sDDId, sHandleId) {
20137             if (!this.handleIds[sDDId]) {
20138                 this.handleIds[sDDId] = {};
20139             }
20140             this.handleIds[sDDId][sHandleId] = sHandleId;
20141         },
20142
20143         /**
20144          * Utility function to determine if a given element has been
20145          * registered as a drag drop item.
20146          * @method isDragDrop
20147          * @param {String} id the element id to check
20148          * @return {boolean} true if this element is a DragDrop item,
20149          * false otherwise
20150          * @static
20151          */
20152         isDragDrop: function(id) {
20153             return ( this.getDDById(id) ) ? true : false;
20154         },
20155
20156         /**
20157          * Returns the drag and drop instances that are in all groups the
20158          * passed in instance belongs to.
20159          * @method getRelated
20160          * @param {DragDrop} p_oDD the obj to get related data for
20161          * @param {boolean} bTargetsOnly if true, only return targetable objs
20162          * @return {DragDrop[]} the related instances
20163          * @static
20164          */
20165         getRelated: function(p_oDD, bTargetsOnly) {
20166             var oDDs = [];
20167             for (var i in p_oDD.groups) {
20168                 for (j in this.ids[i]) {
20169                     var dd = this.ids[i][j];
20170                     if (! this.isTypeOfDD(dd)) {
20171                         continue;
20172                     }
20173                     if (!bTargetsOnly || dd.isTarget) {
20174                         oDDs[oDDs.length] = dd;
20175                     }
20176                 }
20177             }
20178
20179             return oDDs;
20180         },
20181
20182         /**
20183          * Returns true if the specified dd target is a legal target for
20184          * the specifice drag obj
20185          * @method isLegalTarget
20186          * @param {DragDrop} the drag obj
20187          * @param {DragDrop} the target
20188          * @return {boolean} true if the target is a legal target for the
20189          * dd obj
20190          * @static
20191          */
20192         isLegalTarget: function (oDD, oTargetDD) {
20193             var targets = this.getRelated(oDD, true);
20194             for (var i=0, len=targets.length;i<len;++i) {
20195                 if (targets[i].id == oTargetDD.id) {
20196                     return true;
20197                 }
20198             }
20199
20200             return false;
20201         },
20202
20203         /**
20204          * My goal is to be able to transparently determine if an object is
20205          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20206          * returns "object", oDD.constructor.toString() always returns
20207          * "DragDrop" and not the name of the subclass.  So for now it just
20208          * evaluates a well-known variable in DragDrop.
20209          * @method isTypeOfDD
20210          * @param {Object} the object to evaluate
20211          * @return {boolean} true if typeof oDD = DragDrop
20212          * @static
20213          */
20214         isTypeOfDD: function (oDD) {
20215             return (oDD && oDD.__ygDragDrop);
20216         },
20217
20218         /**
20219          * Utility function to determine if a given element has been
20220          * registered as a drag drop handle for the given Drag Drop object.
20221          * @method isHandle
20222          * @param {String} id the element id to check
20223          * @return {boolean} true if this element is a DragDrop handle, false
20224          * otherwise
20225          * @static
20226          */
20227         isHandle: function(sDDId, sHandleId) {
20228             return ( this.handleIds[sDDId] &&
20229                             this.handleIds[sDDId][sHandleId] );
20230         },
20231
20232         /**
20233          * Returns the DragDrop instance for a given id
20234          * @method getDDById
20235          * @param {String} id the id of the DragDrop object
20236          * @return {DragDrop} the drag drop object, null if it is not found
20237          * @static
20238          */
20239         getDDById: function(id) {
20240             for (var i in this.ids) {
20241                 if (this.ids[i][id]) {
20242                     return this.ids[i][id];
20243                 }
20244             }
20245             return null;
20246         },
20247
20248         /**
20249          * Fired after a registered DragDrop object gets the mousedown event.
20250          * Sets up the events required to track the object being dragged
20251          * @method handleMouseDown
20252          * @param {Event} e the event
20253          * @param oDD the DragDrop object being dragged
20254          * @private
20255          * @static
20256          */
20257         handleMouseDown: function(e, oDD) {
20258             if(Roo.QuickTips){
20259                 Roo.QuickTips.disable();
20260             }
20261             this.currentTarget = e.getTarget();
20262
20263             this.dragCurrent = oDD;
20264
20265             var el = oDD.getEl();
20266
20267             // track start position
20268             this.startX = e.getPageX();
20269             this.startY = e.getPageY();
20270
20271             this.deltaX = this.startX - el.offsetLeft;
20272             this.deltaY = this.startY - el.offsetTop;
20273
20274             this.dragThreshMet = false;
20275
20276             this.clickTimeout = setTimeout(
20277                     function() {
20278                         var DDM = Roo.dd.DDM;
20279                         DDM.startDrag(DDM.startX, DDM.startY);
20280                     },
20281                     this.clickTimeThresh );
20282         },
20283
20284         /**
20285          * Fired when either the drag pixel threshol or the mousedown hold
20286          * time threshold has been met.
20287          * @method startDrag
20288          * @param x {int} the X position of the original mousedown
20289          * @param y {int} the Y position of the original mousedown
20290          * @static
20291          */
20292         startDrag: function(x, y) {
20293             clearTimeout(this.clickTimeout);
20294             if (this.dragCurrent) {
20295                 this.dragCurrent.b4StartDrag(x, y);
20296                 this.dragCurrent.startDrag(x, y);
20297             }
20298             this.dragThreshMet = true;
20299         },
20300
20301         /**
20302          * Internal function to handle the mouseup event.  Will be invoked
20303          * from the context of the document.
20304          * @method handleMouseUp
20305          * @param {Event} e the event
20306          * @private
20307          * @static
20308          */
20309         handleMouseUp: function(e) {
20310
20311             if(Roo.QuickTips){
20312                 Roo.QuickTips.enable();
20313             }
20314             if (! this.dragCurrent) {
20315                 return;
20316             }
20317
20318             clearTimeout(this.clickTimeout);
20319
20320             if (this.dragThreshMet) {
20321                 this.fireEvents(e, true);
20322             } else {
20323             }
20324
20325             this.stopDrag(e);
20326
20327             this.stopEvent(e);
20328         },
20329
20330         /**
20331          * Utility to stop event propagation and event default, if these
20332          * features are turned on.
20333          * @method stopEvent
20334          * @param {Event} e the event as returned by this.getEvent()
20335          * @static
20336          */
20337         stopEvent: function(e){
20338             if(this.stopPropagation) {
20339                 e.stopPropagation();
20340             }
20341
20342             if (this.preventDefault) {
20343                 e.preventDefault();
20344             }
20345         },
20346
20347         /**
20348          * Internal function to clean up event handlers after the drag
20349          * operation is complete
20350          * @method stopDrag
20351          * @param {Event} e the event
20352          * @private
20353          * @static
20354          */
20355         stopDrag: function(e) {
20356             // Fire the drag end event for the item that was dragged
20357             if (this.dragCurrent) {
20358                 if (this.dragThreshMet) {
20359                     this.dragCurrent.b4EndDrag(e);
20360                     this.dragCurrent.endDrag(e);
20361                 }
20362
20363                 this.dragCurrent.onMouseUp(e);
20364             }
20365
20366             this.dragCurrent = null;
20367             this.dragOvers = {};
20368         },
20369
20370         /**
20371          * Internal function to handle the mousemove event.  Will be invoked
20372          * from the context of the html element.
20373          *
20374          * @TODO figure out what we can do about mouse events lost when the
20375          * user drags objects beyond the window boundary.  Currently we can
20376          * detect this in internet explorer by verifying that the mouse is
20377          * down during the mousemove event.  Firefox doesn't give us the
20378          * button state on the mousemove event.
20379          * @method handleMouseMove
20380          * @param {Event} e the event
20381          * @private
20382          * @static
20383          */
20384         handleMouseMove: function(e) {
20385             if (! this.dragCurrent) {
20386                 return true;
20387             }
20388
20389             // var button = e.which || e.button;
20390
20391             // check for IE mouseup outside of page boundary
20392             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20393                 this.stopEvent(e);
20394                 return this.handleMouseUp(e);
20395             }
20396
20397             if (!this.dragThreshMet) {
20398                 var diffX = Math.abs(this.startX - e.getPageX());
20399                 var diffY = Math.abs(this.startY - e.getPageY());
20400                 if (diffX > this.clickPixelThresh ||
20401                             diffY > this.clickPixelThresh) {
20402                     this.startDrag(this.startX, this.startY);
20403                 }
20404             }
20405
20406             if (this.dragThreshMet) {
20407                 this.dragCurrent.b4Drag(e);
20408                 this.dragCurrent.onDrag(e);
20409                 if(!this.dragCurrent.moveOnly){
20410                     this.fireEvents(e, false);
20411                 }
20412             }
20413
20414             this.stopEvent(e);
20415
20416             return true;
20417         },
20418
20419         /**
20420          * Iterates over all of the DragDrop elements to find ones we are
20421          * hovering over or dropping on
20422          * @method fireEvents
20423          * @param {Event} e the event
20424          * @param {boolean} isDrop is this a drop op or a mouseover op?
20425          * @private
20426          * @static
20427          */
20428         fireEvents: function(e, isDrop) {
20429             var dc = this.dragCurrent;
20430
20431             // If the user did the mouse up outside of the window, we could
20432             // get here even though we have ended the drag.
20433             if (!dc || dc.isLocked()) {
20434                 return;
20435             }
20436
20437             var pt = e.getPoint();
20438
20439             // cache the previous dragOver array
20440             var oldOvers = [];
20441
20442             var outEvts   = [];
20443             var overEvts  = [];
20444             var dropEvts  = [];
20445             var enterEvts = [];
20446
20447             // Check to see if the object(s) we were hovering over is no longer
20448             // being hovered over so we can fire the onDragOut event
20449             for (var i in this.dragOvers) {
20450
20451                 var ddo = this.dragOvers[i];
20452
20453                 if (! this.isTypeOfDD(ddo)) {
20454                     continue;
20455                 }
20456
20457                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20458                     outEvts.push( ddo );
20459                 }
20460
20461                 oldOvers[i] = true;
20462                 delete this.dragOvers[i];
20463             }
20464
20465             for (var sGroup in dc.groups) {
20466
20467                 if ("string" != typeof sGroup) {
20468                     continue;
20469                 }
20470
20471                 for (i in this.ids[sGroup]) {
20472                     var oDD = this.ids[sGroup][i];
20473                     if (! this.isTypeOfDD(oDD)) {
20474                         continue;
20475                     }
20476
20477                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20478                         if (this.isOverTarget(pt, oDD, this.mode)) {
20479                             // look for drop interactions
20480                             if (isDrop) {
20481                                 dropEvts.push( oDD );
20482                             // look for drag enter and drag over interactions
20483                             } else {
20484
20485                                 // initial drag over: dragEnter fires
20486                                 if (!oldOvers[oDD.id]) {
20487                                     enterEvts.push( oDD );
20488                                 // subsequent drag overs: dragOver fires
20489                                 } else {
20490                                     overEvts.push( oDD );
20491                                 }
20492
20493                                 this.dragOvers[oDD.id] = oDD;
20494                             }
20495                         }
20496                     }
20497                 }
20498             }
20499
20500             if (this.mode) {
20501                 if (outEvts.length) {
20502                     dc.b4DragOut(e, outEvts);
20503                     dc.onDragOut(e, outEvts);
20504                 }
20505
20506                 if (enterEvts.length) {
20507                     dc.onDragEnter(e, enterEvts);
20508                 }
20509
20510                 if (overEvts.length) {
20511                     dc.b4DragOver(e, overEvts);
20512                     dc.onDragOver(e, overEvts);
20513                 }
20514
20515                 if (dropEvts.length) {
20516                     dc.b4DragDrop(e, dropEvts);
20517                     dc.onDragDrop(e, dropEvts);
20518                 }
20519
20520             } else {
20521                 // fire dragout events
20522                 var len = 0;
20523                 for (i=0, len=outEvts.length; i<len; ++i) {
20524                     dc.b4DragOut(e, outEvts[i].id);
20525                     dc.onDragOut(e, outEvts[i].id);
20526                 }
20527
20528                 // fire enter events
20529                 for (i=0,len=enterEvts.length; i<len; ++i) {
20530                     // dc.b4DragEnter(e, oDD.id);
20531                     dc.onDragEnter(e, enterEvts[i].id);
20532                 }
20533
20534                 // fire over events
20535                 for (i=0,len=overEvts.length; i<len; ++i) {
20536                     dc.b4DragOver(e, overEvts[i].id);
20537                     dc.onDragOver(e, overEvts[i].id);
20538                 }
20539
20540                 // fire drop events
20541                 for (i=0, len=dropEvts.length; i<len; ++i) {
20542                     dc.b4DragDrop(e, dropEvts[i].id);
20543                     dc.onDragDrop(e, dropEvts[i].id);
20544                 }
20545
20546             }
20547
20548             // notify about a drop that did not find a target
20549             if (isDrop && !dropEvts.length) {
20550                 dc.onInvalidDrop(e);
20551             }
20552
20553         },
20554
20555         /**
20556          * Helper function for getting the best match from the list of drag
20557          * and drop objects returned by the drag and drop events when we are
20558          * in INTERSECT mode.  It returns either the first object that the
20559          * cursor is over, or the object that has the greatest overlap with
20560          * the dragged element.
20561          * @method getBestMatch
20562          * @param  {DragDrop[]} dds The array of drag and drop objects
20563          * targeted
20564          * @return {DragDrop}       The best single match
20565          * @static
20566          */
20567         getBestMatch: function(dds) {
20568             var winner = null;
20569             // Return null if the input is not what we expect
20570             //if (!dds || !dds.length || dds.length == 0) {
20571                // winner = null;
20572             // If there is only one item, it wins
20573             //} else if (dds.length == 1) {
20574
20575             var len = dds.length;
20576
20577             if (len == 1) {
20578                 winner = dds[0];
20579             } else {
20580                 // Loop through the targeted items
20581                 for (var i=0; i<len; ++i) {
20582                     var dd = dds[i];
20583                     // If the cursor is over the object, it wins.  If the
20584                     // cursor is over multiple matches, the first one we come
20585                     // to wins.
20586                     if (dd.cursorIsOver) {
20587                         winner = dd;
20588                         break;
20589                     // Otherwise the object with the most overlap wins
20590                     } else {
20591                         if (!winner ||
20592                             winner.overlap.getArea() < dd.overlap.getArea()) {
20593                             winner = dd;
20594                         }
20595                     }
20596                 }
20597             }
20598
20599             return winner;
20600         },
20601
20602         /**
20603          * Refreshes the cache of the top-left and bottom-right points of the
20604          * drag and drop objects in the specified group(s).  This is in the
20605          * format that is stored in the drag and drop instance, so typical
20606          * usage is:
20607          * <code>
20608          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20609          * </code>
20610          * Alternatively:
20611          * <code>
20612          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20613          * </code>
20614          * @TODO this really should be an indexed array.  Alternatively this
20615          * method could accept both.
20616          * @method refreshCache
20617          * @param {Object} groups an associative array of groups to refresh
20618          * @static
20619          */
20620         refreshCache: function(groups) {
20621             for (var sGroup in groups) {
20622                 if ("string" != typeof sGroup) {
20623                     continue;
20624                 }
20625                 for (var i in this.ids[sGroup]) {
20626                     var oDD = this.ids[sGroup][i];
20627
20628                     if (this.isTypeOfDD(oDD)) {
20629                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20630                         var loc = this.getLocation(oDD);
20631                         if (loc) {
20632                             this.locationCache[oDD.id] = loc;
20633                         } else {
20634                             delete this.locationCache[oDD.id];
20635                             // this will unregister the drag and drop object if
20636                             // the element is not in a usable state
20637                             // oDD.unreg();
20638                         }
20639                     }
20640                 }
20641             }
20642         },
20643
20644         /**
20645          * This checks to make sure an element exists and is in the DOM.  The
20646          * main purpose is to handle cases where innerHTML is used to remove
20647          * drag and drop objects from the DOM.  IE provides an 'unspecified
20648          * error' when trying to access the offsetParent of such an element
20649          * @method verifyEl
20650          * @param {HTMLElement} el the element to check
20651          * @return {boolean} true if the element looks usable
20652          * @static
20653          */
20654         verifyEl: function(el) {
20655             if (el) {
20656                 var parent;
20657                 if(Roo.isIE){
20658                     try{
20659                         parent = el.offsetParent;
20660                     }catch(e){}
20661                 }else{
20662                     parent = el.offsetParent;
20663                 }
20664                 if (parent) {
20665                     return true;
20666                 }
20667             }
20668
20669             return false;
20670         },
20671
20672         /**
20673          * Returns a Region object containing the drag and drop element's position
20674          * and size, including the padding configured for it
20675          * @method getLocation
20676          * @param {DragDrop} oDD the drag and drop object to get the
20677          *                       location for
20678          * @return {Roo.lib.Region} a Region object representing the total area
20679          *                             the element occupies, including any padding
20680          *                             the instance is configured for.
20681          * @static
20682          */
20683         getLocation: function(oDD) {
20684             if (! this.isTypeOfDD(oDD)) {
20685                 return null;
20686             }
20687
20688             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20689
20690             try {
20691                 pos= Roo.lib.Dom.getXY(el);
20692             } catch (e) { }
20693
20694             if (!pos) {
20695                 return null;
20696             }
20697
20698             x1 = pos[0];
20699             x2 = x1 + el.offsetWidth;
20700             y1 = pos[1];
20701             y2 = y1 + el.offsetHeight;
20702
20703             t = y1 - oDD.padding[0];
20704             r = x2 + oDD.padding[1];
20705             b = y2 + oDD.padding[2];
20706             l = x1 - oDD.padding[3];
20707
20708             return new Roo.lib.Region( t, r, b, l );
20709         },
20710
20711         /**
20712          * Checks the cursor location to see if it over the target
20713          * @method isOverTarget
20714          * @param {Roo.lib.Point} pt The point to evaluate
20715          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20716          * @return {boolean} true if the mouse is over the target
20717          * @private
20718          * @static
20719          */
20720         isOverTarget: function(pt, oTarget, intersect) {
20721             // use cache if available
20722             var loc = this.locationCache[oTarget.id];
20723             if (!loc || !this.useCache) {
20724                 loc = this.getLocation(oTarget);
20725                 this.locationCache[oTarget.id] = loc;
20726
20727             }
20728
20729             if (!loc) {
20730                 return false;
20731             }
20732
20733             oTarget.cursorIsOver = loc.contains( pt );
20734
20735             // DragDrop is using this as a sanity check for the initial mousedown
20736             // in this case we are done.  In POINT mode, if the drag obj has no
20737             // contraints, we are also done. Otherwise we need to evaluate the
20738             // location of the target as related to the actual location of the
20739             // dragged element.
20740             var dc = this.dragCurrent;
20741             if (!dc || !dc.getTargetCoord ||
20742                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20743                 return oTarget.cursorIsOver;
20744             }
20745
20746             oTarget.overlap = null;
20747
20748             // Get the current location of the drag element, this is the
20749             // location of the mouse event less the delta that represents
20750             // where the original mousedown happened on the element.  We
20751             // need to consider constraints and ticks as well.
20752             var pos = dc.getTargetCoord(pt.x, pt.y);
20753
20754             var el = dc.getDragEl();
20755             var curRegion = new Roo.lib.Region( pos.y,
20756                                                    pos.x + el.offsetWidth,
20757                                                    pos.y + el.offsetHeight,
20758                                                    pos.x );
20759
20760             var overlap = curRegion.intersect(loc);
20761
20762             if (overlap) {
20763                 oTarget.overlap = overlap;
20764                 return (intersect) ? true : oTarget.cursorIsOver;
20765             } else {
20766                 return false;
20767             }
20768         },
20769
20770         /**
20771          * unload event handler
20772          * @method _onUnload
20773          * @private
20774          * @static
20775          */
20776         _onUnload: function(e, me) {
20777             Roo.dd.DragDropMgr.unregAll();
20778         },
20779
20780         /**
20781          * Cleans up the drag and drop events and objects.
20782          * @method unregAll
20783          * @private
20784          * @static
20785          */
20786         unregAll: function() {
20787
20788             if (this.dragCurrent) {
20789                 this.stopDrag();
20790                 this.dragCurrent = null;
20791             }
20792
20793             this._execOnAll("unreg", []);
20794
20795             for (i in this.elementCache) {
20796                 delete this.elementCache[i];
20797             }
20798
20799             this.elementCache = {};
20800             this.ids = {};
20801         },
20802
20803         /**
20804          * A cache of DOM elements
20805          * @property elementCache
20806          * @private
20807          * @static
20808          */
20809         elementCache: {},
20810
20811         /**
20812          * Get the wrapper for the DOM element specified
20813          * @method getElWrapper
20814          * @param {String} id the id of the element to get
20815          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20816          * @private
20817          * @deprecated This wrapper isn't that useful
20818          * @static
20819          */
20820         getElWrapper: function(id) {
20821             var oWrapper = this.elementCache[id];
20822             if (!oWrapper || !oWrapper.el) {
20823                 oWrapper = this.elementCache[id] =
20824                     new this.ElementWrapper(Roo.getDom(id));
20825             }
20826             return oWrapper;
20827         },
20828
20829         /**
20830          * Returns the actual DOM element
20831          * @method getElement
20832          * @param {String} id the id of the elment to get
20833          * @return {Object} The element
20834          * @deprecated use Roo.getDom instead
20835          * @static
20836          */
20837         getElement: function(id) {
20838             return Roo.getDom(id);
20839         },
20840
20841         /**
20842          * Returns the style property for the DOM element (i.e.,
20843          * document.getElById(id).style)
20844          * @method getCss
20845          * @param {String} id the id of the elment to get
20846          * @return {Object} The style property of the element
20847          * @deprecated use Roo.getDom instead
20848          * @static
20849          */
20850         getCss: function(id) {
20851             var el = Roo.getDom(id);
20852             return (el) ? el.style : null;
20853         },
20854
20855         /**
20856          * Inner class for cached elements
20857          * @class DragDropMgr.ElementWrapper
20858          * @for DragDropMgr
20859          * @private
20860          * @deprecated
20861          */
20862         ElementWrapper: function(el) {
20863                 /**
20864                  * The element
20865                  * @property el
20866                  */
20867                 this.el = el || null;
20868                 /**
20869                  * The element id
20870                  * @property id
20871                  */
20872                 this.id = this.el && el.id;
20873                 /**
20874                  * A reference to the style property
20875                  * @property css
20876                  */
20877                 this.css = this.el && el.style;
20878             },
20879
20880         /**
20881          * Returns the X position of an html element
20882          * @method getPosX
20883          * @param el the element for which to get the position
20884          * @return {int} the X coordinate
20885          * @for DragDropMgr
20886          * @deprecated use Roo.lib.Dom.getX instead
20887          * @static
20888          */
20889         getPosX: function(el) {
20890             return Roo.lib.Dom.getX(el);
20891         },
20892
20893         /**
20894          * Returns the Y position of an html element
20895          * @method getPosY
20896          * @param el the element for which to get the position
20897          * @return {int} the Y coordinate
20898          * @deprecated use Roo.lib.Dom.getY instead
20899          * @static
20900          */
20901         getPosY: function(el) {
20902             return Roo.lib.Dom.getY(el);
20903         },
20904
20905         /**
20906          * Swap two nodes.  In IE, we use the native method, for others we
20907          * emulate the IE behavior
20908          * @method swapNode
20909          * @param n1 the first node to swap
20910          * @param n2 the other node to swap
20911          * @static
20912          */
20913         swapNode: function(n1, n2) {
20914             if (n1.swapNode) {
20915                 n1.swapNode(n2);
20916             } else {
20917                 var p = n2.parentNode;
20918                 var s = n2.nextSibling;
20919
20920                 if (s == n1) {
20921                     p.insertBefore(n1, n2);
20922                 } else if (n2 == n1.nextSibling) {
20923                     p.insertBefore(n2, n1);
20924                 } else {
20925                     n1.parentNode.replaceChild(n2, n1);
20926                     p.insertBefore(n1, s);
20927                 }
20928             }
20929         },
20930
20931         /**
20932          * Returns the current scroll position
20933          * @method getScroll
20934          * @private
20935          * @static
20936          */
20937         getScroll: function () {
20938             var t, l, dde=document.documentElement, db=document.body;
20939             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20940                 t = dde.scrollTop;
20941                 l = dde.scrollLeft;
20942             } else if (db) {
20943                 t = db.scrollTop;
20944                 l = db.scrollLeft;
20945             } else {
20946
20947             }
20948             return { top: t, left: l };
20949         },
20950
20951         /**
20952          * Returns the specified element style property
20953          * @method getStyle
20954          * @param {HTMLElement} el          the element
20955          * @param {string}      styleProp   the style property
20956          * @return {string} The value of the style property
20957          * @deprecated use Roo.lib.Dom.getStyle
20958          * @static
20959          */
20960         getStyle: function(el, styleProp) {
20961             return Roo.fly(el).getStyle(styleProp);
20962         },
20963
20964         /**
20965          * Gets the scrollTop
20966          * @method getScrollTop
20967          * @return {int} the document's scrollTop
20968          * @static
20969          */
20970         getScrollTop: function () { return this.getScroll().top; },
20971
20972         /**
20973          * Gets the scrollLeft
20974          * @method getScrollLeft
20975          * @return {int} the document's scrollTop
20976          * @static
20977          */
20978         getScrollLeft: function () { return this.getScroll().left; },
20979
20980         /**
20981          * Sets the x/y position of an element to the location of the
20982          * target element.
20983          * @method moveToEl
20984          * @param {HTMLElement} moveEl      The element to move
20985          * @param {HTMLElement} targetEl    The position reference element
20986          * @static
20987          */
20988         moveToEl: function (moveEl, targetEl) {
20989             var aCoord = Roo.lib.Dom.getXY(targetEl);
20990             Roo.lib.Dom.setXY(moveEl, aCoord);
20991         },
20992
20993         /**
20994          * Numeric array sort function
20995          * @method numericSort
20996          * @static
20997          */
20998         numericSort: function(a, b) { return (a - b); },
20999
21000         /**
21001          * Internal counter
21002          * @property _timeoutCount
21003          * @private
21004          * @static
21005          */
21006         _timeoutCount: 0,
21007
21008         /**
21009          * Trying to make the load order less important.  Without this we get
21010          * an error if this file is loaded before the Event Utility.
21011          * @method _addListeners
21012          * @private
21013          * @static
21014          */
21015         _addListeners: function() {
21016             var DDM = Roo.dd.DDM;
21017             if ( Roo.lib.Event && document ) {
21018                 DDM._onLoad();
21019             } else {
21020                 if (DDM._timeoutCount > 2000) {
21021                 } else {
21022                     setTimeout(DDM._addListeners, 10);
21023                     if (document && document.body) {
21024                         DDM._timeoutCount += 1;
21025                     }
21026                 }
21027             }
21028         },
21029
21030         /**
21031          * Recursively searches the immediate parent and all child nodes for
21032          * the handle element in order to determine wheter or not it was
21033          * clicked.
21034          * @method handleWasClicked
21035          * @param node the html element to inspect
21036          * @static
21037          */
21038         handleWasClicked: function(node, id) {
21039             if (this.isHandle(id, node.id)) {
21040                 return true;
21041             } else {
21042                 // check to see if this is a text node child of the one we want
21043                 var p = node.parentNode;
21044
21045                 while (p) {
21046                     if (this.isHandle(id, p.id)) {
21047                         return true;
21048                     } else {
21049                         p = p.parentNode;
21050                     }
21051                 }
21052             }
21053
21054             return false;
21055         }
21056
21057     };
21058
21059 }();
21060
21061 // shorter alias, save a few bytes
21062 Roo.dd.DDM = Roo.dd.DragDropMgr;
21063 Roo.dd.DDM._addListeners();
21064
21065 }/*
21066  * Based on:
21067  * Ext JS Library 1.1.1
21068  * Copyright(c) 2006-2007, Ext JS, LLC.
21069  *
21070  * Originally Released Under LGPL - original licence link has changed is not relivant.
21071  *
21072  * Fork - LGPL
21073  * <script type="text/javascript">
21074  */
21075
21076 /**
21077  * @class Roo.dd.DD
21078  * A DragDrop implementation where the linked element follows the
21079  * mouse cursor during a drag.
21080  * @extends Roo.dd.DragDrop
21081  * @constructor
21082  * @param {String} id the id of the linked element
21083  * @param {String} sGroup the group of related DragDrop items
21084  * @param {object} config an object containing configurable attributes
21085  *                Valid properties for DD:
21086  *                    scroll
21087  */
21088 Roo.dd.DD = function(id, sGroup, config) {
21089     if (id) {
21090         this.init(id, sGroup, config);
21091     }
21092 };
21093
21094 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21095
21096     /**
21097      * When set to true, the utility automatically tries to scroll the browser
21098      * window wehn a drag and drop element is dragged near the viewport boundary.
21099      * Defaults to true.
21100      * @property scroll
21101      * @type boolean
21102      */
21103     scroll: true,
21104
21105     /**
21106      * Sets the pointer offset to the distance between the linked element's top
21107      * left corner and the location the element was clicked
21108      * @method autoOffset
21109      * @param {int} iPageX the X coordinate of the click
21110      * @param {int} iPageY the Y coordinate of the click
21111      */
21112     autoOffset: function(iPageX, iPageY) {
21113         var x = iPageX - this.startPageX;
21114         var y = iPageY - this.startPageY;
21115         this.setDelta(x, y);
21116     },
21117
21118     /**
21119      * Sets the pointer offset.  You can call this directly to force the
21120      * offset to be in a particular location (e.g., pass in 0,0 to set it
21121      * to the center of the object)
21122      * @method setDelta
21123      * @param {int} iDeltaX the distance from the left
21124      * @param {int} iDeltaY the distance from the top
21125      */
21126     setDelta: function(iDeltaX, iDeltaY) {
21127         this.deltaX = iDeltaX;
21128         this.deltaY = iDeltaY;
21129     },
21130
21131     /**
21132      * Sets the drag element to the location of the mousedown or click event,
21133      * maintaining the cursor location relative to the location on the element
21134      * that was clicked.  Override this if you want to place the element in a
21135      * location other than where the cursor is.
21136      * @method setDragElPos
21137      * @param {int} iPageX the X coordinate of the mousedown or drag event
21138      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21139      */
21140     setDragElPos: function(iPageX, iPageY) {
21141         // the first time we do this, we are going to check to make sure
21142         // the element has css positioning
21143
21144         var el = this.getDragEl();
21145         this.alignElWithMouse(el, iPageX, iPageY);
21146     },
21147
21148     /**
21149      * Sets the element to the location of the mousedown or click event,
21150      * maintaining the cursor location relative to the location on the element
21151      * that was clicked.  Override this if you want to place the element in a
21152      * location other than where the cursor is.
21153      * @method alignElWithMouse
21154      * @param {HTMLElement} el the element to move
21155      * @param {int} iPageX the X coordinate of the mousedown or drag event
21156      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21157      */
21158     alignElWithMouse: function(el, iPageX, iPageY) {
21159         var oCoord = this.getTargetCoord(iPageX, iPageY);
21160         var fly = el.dom ? el : Roo.fly(el);
21161         if (!this.deltaSetXY) {
21162             var aCoord = [oCoord.x, oCoord.y];
21163             fly.setXY(aCoord);
21164             var newLeft = fly.getLeft(true);
21165             var newTop  = fly.getTop(true);
21166             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21167         } else {
21168             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21169         }
21170
21171         this.cachePosition(oCoord.x, oCoord.y);
21172         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21173         return oCoord;
21174     },
21175
21176     /**
21177      * Saves the most recent position so that we can reset the constraints and
21178      * tick marks on-demand.  We need to know this so that we can calculate the
21179      * number of pixels the element is offset from its original position.
21180      * @method cachePosition
21181      * @param iPageX the current x position (optional, this just makes it so we
21182      * don't have to look it up again)
21183      * @param iPageY the current y position (optional, this just makes it so we
21184      * don't have to look it up again)
21185      */
21186     cachePosition: function(iPageX, iPageY) {
21187         if (iPageX) {
21188             this.lastPageX = iPageX;
21189             this.lastPageY = iPageY;
21190         } else {
21191             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21192             this.lastPageX = aCoord[0];
21193             this.lastPageY = aCoord[1];
21194         }
21195     },
21196
21197     /**
21198      * Auto-scroll the window if the dragged object has been moved beyond the
21199      * visible window boundary.
21200      * @method autoScroll
21201      * @param {int} x the drag element's x position
21202      * @param {int} y the drag element's y position
21203      * @param {int} h the height of the drag element
21204      * @param {int} w the width of the drag element
21205      * @private
21206      */
21207     autoScroll: function(x, y, h, w) {
21208
21209         if (this.scroll) {
21210             // The client height
21211             var clientH = Roo.lib.Dom.getViewWidth();
21212
21213             // The client width
21214             var clientW = Roo.lib.Dom.getViewHeight();
21215
21216             // The amt scrolled down
21217             var st = this.DDM.getScrollTop();
21218
21219             // The amt scrolled right
21220             var sl = this.DDM.getScrollLeft();
21221
21222             // Location of the bottom of the element
21223             var bot = h + y;
21224
21225             // Location of the right of the element
21226             var right = w + x;
21227
21228             // The distance from the cursor to the bottom of the visible area,
21229             // adjusted so that we don't scroll if the cursor is beyond the
21230             // element drag constraints
21231             var toBot = (clientH + st - y - this.deltaY);
21232
21233             // The distance from the cursor to the right of the visible area
21234             var toRight = (clientW + sl - x - this.deltaX);
21235
21236
21237             // How close to the edge the cursor must be before we scroll
21238             // var thresh = (document.all) ? 100 : 40;
21239             var thresh = 40;
21240
21241             // How many pixels to scroll per autoscroll op.  This helps to reduce
21242             // clunky scrolling. IE is more sensitive about this ... it needs this
21243             // value to be higher.
21244             var scrAmt = (document.all) ? 80 : 30;
21245
21246             // Scroll down if we are near the bottom of the visible page and the
21247             // obj extends below the crease
21248             if ( bot > clientH && toBot < thresh ) {
21249                 window.scrollTo(sl, st + scrAmt);
21250             }
21251
21252             // Scroll up if the window is scrolled down and the top of the object
21253             // goes above the top border
21254             if ( y < st && st > 0 && y - st < thresh ) {
21255                 window.scrollTo(sl, st - scrAmt);
21256             }
21257
21258             // Scroll right if the obj is beyond the right border and the cursor is
21259             // near the border.
21260             if ( right > clientW && toRight < thresh ) {
21261                 window.scrollTo(sl + scrAmt, st);
21262             }
21263
21264             // Scroll left if the window has been scrolled to the right and the obj
21265             // extends past the left border
21266             if ( x < sl && sl > 0 && x - sl < thresh ) {
21267                 window.scrollTo(sl - scrAmt, st);
21268             }
21269         }
21270     },
21271
21272     /**
21273      * Finds the location the element should be placed if we want to move
21274      * it to where the mouse location less the click offset would place us.
21275      * @method getTargetCoord
21276      * @param {int} iPageX the X coordinate of the click
21277      * @param {int} iPageY the Y coordinate of the click
21278      * @return an object that contains the coordinates (Object.x and Object.y)
21279      * @private
21280      */
21281     getTargetCoord: function(iPageX, iPageY) {
21282
21283
21284         var x = iPageX - this.deltaX;
21285         var y = iPageY - this.deltaY;
21286
21287         if (this.constrainX) {
21288             if (x < this.minX) { x = this.minX; }
21289             if (x > this.maxX) { x = this.maxX; }
21290         }
21291
21292         if (this.constrainY) {
21293             if (y < this.minY) { y = this.minY; }
21294             if (y > this.maxY) { y = this.maxY; }
21295         }
21296
21297         x = this.getTick(x, this.xTicks);
21298         y = this.getTick(y, this.yTicks);
21299
21300
21301         return {x:x, y:y};
21302     },
21303
21304     /*
21305      * Sets up config options specific to this class. Overrides
21306      * Roo.dd.DragDrop, but all versions of this method through the
21307      * inheritance chain are called
21308      */
21309     applyConfig: function() {
21310         Roo.dd.DD.superclass.applyConfig.call(this);
21311         this.scroll = (this.config.scroll !== false);
21312     },
21313
21314     /*
21315      * Event that fires prior to the onMouseDown event.  Overrides
21316      * Roo.dd.DragDrop.
21317      */
21318     b4MouseDown: function(e) {
21319         // this.resetConstraints();
21320         this.autoOffset(e.getPageX(),
21321                             e.getPageY());
21322     },
21323
21324     /*
21325      * Event that fires prior to the onDrag event.  Overrides
21326      * Roo.dd.DragDrop.
21327      */
21328     b4Drag: function(e) {
21329         this.setDragElPos(e.getPageX(),
21330                             e.getPageY());
21331     },
21332
21333     toString: function() {
21334         return ("DD " + this.id);
21335     }
21336
21337     //////////////////////////////////////////////////////////////////////////
21338     // Debugging ygDragDrop events that can be overridden
21339     //////////////////////////////////////////////////////////////////////////
21340     /*
21341     startDrag: function(x, y) {
21342     },
21343
21344     onDrag: function(e) {
21345     },
21346
21347     onDragEnter: function(e, id) {
21348     },
21349
21350     onDragOver: function(e, id) {
21351     },
21352
21353     onDragOut: function(e, id) {
21354     },
21355
21356     onDragDrop: function(e, id) {
21357     },
21358
21359     endDrag: function(e) {
21360     }
21361
21362     */
21363
21364 });/*
21365  * Based on:
21366  * Ext JS Library 1.1.1
21367  * Copyright(c) 2006-2007, Ext JS, LLC.
21368  *
21369  * Originally Released Under LGPL - original licence link has changed is not relivant.
21370  *
21371  * Fork - LGPL
21372  * <script type="text/javascript">
21373  */
21374
21375 /**
21376  * @class Roo.dd.DDProxy
21377  * A DragDrop implementation that inserts an empty, bordered div into
21378  * the document that follows the cursor during drag operations.  At the time of
21379  * the click, the frame div is resized to the dimensions of the linked html
21380  * element, and moved to the exact location of the linked element.
21381  *
21382  * References to the "frame" element refer to the single proxy element that
21383  * was created to be dragged in place of all DDProxy elements on the
21384  * page.
21385  *
21386  * @extends Roo.dd.DD
21387  * @constructor
21388  * @param {String} id the id of the linked html element
21389  * @param {String} sGroup the group of related DragDrop objects
21390  * @param {object} config an object containing configurable attributes
21391  *                Valid properties for DDProxy in addition to those in DragDrop:
21392  *                   resizeFrame, centerFrame, dragElId
21393  */
21394 Roo.dd.DDProxy = function(id, sGroup, config) {
21395     if (id) {
21396         this.init(id, sGroup, config);
21397         this.initFrame();
21398     }
21399 };
21400
21401 /**
21402  * The default drag frame div id
21403  * @property Roo.dd.DDProxy.dragElId
21404  * @type String
21405  * @static
21406  */
21407 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21408
21409 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21410
21411     /**
21412      * By default we resize the drag frame to be the same size as the element
21413      * we want to drag (this is to get the frame effect).  We can turn it off
21414      * if we want a different behavior.
21415      * @property resizeFrame
21416      * @type boolean
21417      */
21418     resizeFrame: true,
21419
21420     /**
21421      * By default the frame is positioned exactly where the drag element is, so
21422      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21423      * you do not have constraints on the obj is to have the drag frame centered
21424      * around the cursor.  Set centerFrame to true for this effect.
21425      * @property centerFrame
21426      * @type boolean
21427      */
21428     centerFrame: false,
21429
21430     /**
21431      * Creates the proxy element if it does not yet exist
21432      * @method createFrame
21433      */
21434     createFrame: function() {
21435         var self = this;
21436         var body = document.body;
21437
21438         if (!body || !body.firstChild) {
21439             setTimeout( function() { self.createFrame(); }, 50 );
21440             return;
21441         }
21442
21443         var div = this.getDragEl();
21444
21445         if (!div) {
21446             div    = document.createElement("div");
21447             div.id = this.dragElId;
21448             var s  = div.style;
21449
21450             s.position   = "absolute";
21451             s.visibility = "hidden";
21452             s.cursor     = "move";
21453             s.border     = "2px solid #aaa";
21454             s.zIndex     = 999;
21455
21456             // appendChild can blow up IE if invoked prior to the window load event
21457             // while rendering a table.  It is possible there are other scenarios
21458             // that would cause this to happen as well.
21459             body.insertBefore(div, body.firstChild);
21460         }
21461     },
21462
21463     /**
21464      * Initialization for the drag frame element.  Must be called in the
21465      * constructor of all subclasses
21466      * @method initFrame
21467      */
21468     initFrame: function() {
21469         this.createFrame();
21470     },
21471
21472     applyConfig: function() {
21473         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21474
21475         this.resizeFrame = (this.config.resizeFrame !== false);
21476         this.centerFrame = (this.config.centerFrame);
21477         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21478     },
21479
21480     /**
21481      * Resizes the drag frame to the dimensions of the clicked object, positions
21482      * it over the object, and finally displays it
21483      * @method showFrame
21484      * @param {int} iPageX X click position
21485      * @param {int} iPageY Y click position
21486      * @private
21487      */
21488     showFrame: function(iPageX, iPageY) {
21489         var el = this.getEl();
21490         var dragEl = this.getDragEl();
21491         var s = dragEl.style;
21492
21493         this._resizeProxy();
21494
21495         if (this.centerFrame) {
21496             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21497                            Math.round(parseInt(s.height, 10)/2) );
21498         }
21499
21500         this.setDragElPos(iPageX, iPageY);
21501
21502         Roo.fly(dragEl).show();
21503     },
21504
21505     /**
21506      * The proxy is automatically resized to the dimensions of the linked
21507      * element when a drag is initiated, unless resizeFrame is set to false
21508      * @method _resizeProxy
21509      * @private
21510      */
21511     _resizeProxy: function() {
21512         if (this.resizeFrame) {
21513             var el = this.getEl();
21514             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21515         }
21516     },
21517
21518     // overrides Roo.dd.DragDrop
21519     b4MouseDown: function(e) {
21520         var x = e.getPageX();
21521         var y = e.getPageY();
21522         this.autoOffset(x, y);
21523         this.setDragElPos(x, y);
21524     },
21525
21526     // overrides Roo.dd.DragDrop
21527     b4StartDrag: function(x, y) {
21528         // show the drag frame
21529         this.showFrame(x, y);
21530     },
21531
21532     // overrides Roo.dd.DragDrop
21533     b4EndDrag: function(e) {
21534         Roo.fly(this.getDragEl()).hide();
21535     },
21536
21537     // overrides Roo.dd.DragDrop
21538     // By default we try to move the element to the last location of the frame.
21539     // This is so that the default behavior mirrors that of Roo.dd.DD.
21540     endDrag: function(e) {
21541
21542         var lel = this.getEl();
21543         var del = this.getDragEl();
21544
21545         // Show the drag frame briefly so we can get its position
21546         del.style.visibility = "";
21547
21548         this.beforeMove();
21549         // Hide the linked element before the move to get around a Safari
21550         // rendering bug.
21551         lel.style.visibility = "hidden";
21552         Roo.dd.DDM.moveToEl(lel, del);
21553         del.style.visibility = "hidden";
21554         lel.style.visibility = "";
21555
21556         this.afterDrag();
21557     },
21558
21559     beforeMove : function(){
21560
21561     },
21562
21563     afterDrag : function(){
21564
21565     },
21566
21567     toString: function() {
21568         return ("DDProxy " + this.id);
21569     }
21570
21571 });
21572 /*
21573  * Based on:
21574  * Ext JS Library 1.1.1
21575  * Copyright(c) 2006-2007, Ext JS, LLC.
21576  *
21577  * Originally Released Under LGPL - original licence link has changed is not relivant.
21578  *
21579  * Fork - LGPL
21580  * <script type="text/javascript">
21581  */
21582
21583  /**
21584  * @class Roo.dd.DDTarget
21585  * A DragDrop implementation that does not move, but can be a drop
21586  * target.  You would get the same result by simply omitting implementation
21587  * for the event callbacks, but this way we reduce the processing cost of the
21588  * event listener and the callbacks.
21589  * @extends Roo.dd.DragDrop
21590  * @constructor
21591  * @param {String} id the id of the element that is a drop target
21592  * @param {String} sGroup the group of related DragDrop objects
21593  * @param {object} config an object containing configurable attributes
21594  *                 Valid properties for DDTarget in addition to those in
21595  *                 DragDrop:
21596  *                    none
21597  */
21598 Roo.dd.DDTarget = function(id, sGroup, config) {
21599     if (id) {
21600         this.initTarget(id, sGroup, config);
21601     }
21602     if (config && (config.listeners || config.events)) { 
21603         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21604             listeners : config.listeners || {}, 
21605             events : config.events || {} 
21606         });    
21607     }
21608 };
21609
21610 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21611 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21612     toString: function() {
21613         return ("DDTarget " + this.id);
21614     }
21615 });
21616 /*
21617  * Based on:
21618  * Ext JS Library 1.1.1
21619  * Copyright(c) 2006-2007, Ext JS, LLC.
21620  *
21621  * Originally Released Under LGPL - original licence link has changed is not relivant.
21622  *
21623  * Fork - LGPL
21624  * <script type="text/javascript">
21625  */
21626  
21627
21628 /**
21629  * @class Roo.dd.ScrollManager
21630  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21631  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21632  * @singleton
21633  */
21634 Roo.dd.ScrollManager = function(){
21635     var ddm = Roo.dd.DragDropMgr;
21636     var els = {};
21637     var dragEl = null;
21638     var proc = {};
21639     
21640     
21641     
21642     var onStop = function(e){
21643         dragEl = null;
21644         clearProc();
21645     };
21646     
21647     var triggerRefresh = function(){
21648         if(ddm.dragCurrent){
21649              ddm.refreshCache(ddm.dragCurrent.groups);
21650         }
21651     };
21652     
21653     var doScroll = function(){
21654         if(ddm.dragCurrent){
21655             var dds = Roo.dd.ScrollManager;
21656             if(!dds.animate){
21657                 if(proc.el.scroll(proc.dir, dds.increment)){
21658                     triggerRefresh();
21659                 }
21660             }else{
21661                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21662             }
21663         }
21664     };
21665     
21666     var clearProc = function(){
21667         if(proc.id){
21668             clearInterval(proc.id);
21669         }
21670         proc.id = 0;
21671         proc.el = null;
21672         proc.dir = "";
21673     };
21674     
21675     var startProc = function(el, dir){
21676          Roo.log('scroll startproc');
21677         clearProc();
21678         proc.el = el;
21679         proc.dir = dir;
21680         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21681     };
21682     
21683     var onFire = function(e, isDrop){
21684        
21685         if(isDrop || !ddm.dragCurrent){ return; }
21686         var dds = Roo.dd.ScrollManager;
21687         if(!dragEl || dragEl != ddm.dragCurrent){
21688             dragEl = ddm.dragCurrent;
21689             // refresh regions on drag start
21690             dds.refreshCache();
21691         }
21692         
21693         var xy = Roo.lib.Event.getXY(e);
21694         var pt = new Roo.lib.Point(xy[0], xy[1]);
21695         for(var id in els){
21696             var el = els[id], r = el._region;
21697             if(r && r.contains(pt) && el.isScrollable()){
21698                 if(r.bottom - pt.y <= dds.thresh){
21699                     if(proc.el != el){
21700                         startProc(el, "down");
21701                     }
21702                     return;
21703                 }else if(r.right - pt.x <= dds.thresh){
21704                     if(proc.el != el){
21705                         startProc(el, "left");
21706                     }
21707                     return;
21708                 }else if(pt.y - r.top <= dds.thresh){
21709                     if(proc.el != el){
21710                         startProc(el, "up");
21711                     }
21712                     return;
21713                 }else if(pt.x - r.left <= dds.thresh){
21714                     if(proc.el != el){
21715                         startProc(el, "right");
21716                     }
21717                     return;
21718                 }
21719             }
21720         }
21721         clearProc();
21722     };
21723     
21724     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21725     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21726     
21727     return {
21728         /**
21729          * Registers new overflow element(s) to auto scroll
21730          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21731          */
21732         register : function(el){
21733             if(el instanceof Array){
21734                 for(var i = 0, len = el.length; i < len; i++) {
21735                         this.register(el[i]);
21736                 }
21737             }else{
21738                 el = Roo.get(el);
21739                 els[el.id] = el;
21740             }
21741             Roo.dd.ScrollManager.els = els;
21742         },
21743         
21744         /**
21745          * Unregisters overflow element(s) so they are no longer scrolled
21746          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21747          */
21748         unregister : function(el){
21749             if(el instanceof Array){
21750                 for(var i = 0, len = el.length; i < len; i++) {
21751                         this.unregister(el[i]);
21752                 }
21753             }else{
21754                 el = Roo.get(el);
21755                 delete els[el.id];
21756             }
21757         },
21758         
21759         /**
21760          * The number of pixels from the edge of a container the pointer needs to be to 
21761          * trigger scrolling (defaults to 25)
21762          * @type Number
21763          */
21764         thresh : 25,
21765         
21766         /**
21767          * The number of pixels to scroll in each scroll increment (defaults to 50)
21768          * @type Number
21769          */
21770         increment : 100,
21771         
21772         /**
21773          * The frequency of scrolls in milliseconds (defaults to 500)
21774          * @type Number
21775          */
21776         frequency : 500,
21777         
21778         /**
21779          * True to animate the scroll (defaults to true)
21780          * @type Boolean
21781          */
21782         animate: true,
21783         
21784         /**
21785          * The animation duration in seconds - 
21786          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21787          * @type Number
21788          */
21789         animDuration: .4,
21790         
21791         /**
21792          * Manually trigger a cache refresh.
21793          */
21794         refreshCache : function(){
21795             for(var id in els){
21796                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21797                     els[id]._region = els[id].getRegion();
21798                 }
21799             }
21800         }
21801     };
21802 }();/*
21803  * Based on:
21804  * Ext JS Library 1.1.1
21805  * Copyright(c) 2006-2007, Ext JS, LLC.
21806  *
21807  * Originally Released Under LGPL - original licence link has changed is not relivant.
21808  *
21809  * Fork - LGPL
21810  * <script type="text/javascript">
21811  */
21812  
21813
21814 /**
21815  * @class Roo.dd.Registry
21816  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21817  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21818  * @singleton
21819  */
21820 Roo.dd.Registry = function(){
21821     var elements = {}; 
21822     var handles = {}; 
21823     var autoIdSeed = 0;
21824
21825     var getId = function(el, autogen){
21826         if(typeof el == "string"){
21827             return el;
21828         }
21829         var id = el.id;
21830         if(!id && autogen !== false){
21831             id = "roodd-" + (++autoIdSeed);
21832             el.id = id;
21833         }
21834         return id;
21835     };
21836     
21837     return {
21838     /**
21839      * Register a drag drop element
21840      * @param {String|HTMLElement} element The id or DOM node to register
21841      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21842      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21843      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21844      * populated in the data object (if applicable):
21845      * <pre>
21846 Value      Description<br />
21847 ---------  ------------------------------------------<br />
21848 handles    Array of DOM nodes that trigger dragging<br />
21849            for the element being registered<br />
21850 isHandle   True if the element passed in triggers<br />
21851            dragging itself, else false
21852 </pre>
21853      */
21854         register : function(el, data){
21855             data = data || {};
21856             if(typeof el == "string"){
21857                 el = document.getElementById(el);
21858             }
21859             data.ddel = el;
21860             elements[getId(el)] = data;
21861             if(data.isHandle !== false){
21862                 handles[data.ddel.id] = data;
21863             }
21864             if(data.handles){
21865                 var hs = data.handles;
21866                 for(var i = 0, len = hs.length; i < len; i++){
21867                         handles[getId(hs[i])] = data;
21868                 }
21869             }
21870         },
21871
21872     /**
21873      * Unregister a drag drop element
21874      * @param {String|HTMLElement}  element The id or DOM node to unregister
21875      */
21876         unregister : function(el){
21877             var id = getId(el, false);
21878             var data = elements[id];
21879             if(data){
21880                 delete elements[id];
21881                 if(data.handles){
21882                     var hs = data.handles;
21883                     for(var i = 0, len = hs.length; i < len; i++){
21884                         delete handles[getId(hs[i], false)];
21885                     }
21886                 }
21887             }
21888         },
21889
21890     /**
21891      * Returns the handle registered for a DOM Node by id
21892      * @param {String|HTMLElement} id The DOM node or id to look up
21893      * @return {Object} handle The custom handle data
21894      */
21895         getHandle : function(id){
21896             if(typeof id != "string"){ // must be element?
21897                 id = id.id;
21898             }
21899             return handles[id];
21900         },
21901
21902     /**
21903      * Returns the handle that is registered for the DOM node that is the target of the event
21904      * @param {Event} e The event
21905      * @return {Object} handle The custom handle data
21906      */
21907         getHandleFromEvent : function(e){
21908             var t = Roo.lib.Event.getTarget(e);
21909             return t ? handles[t.id] : null;
21910         },
21911
21912     /**
21913      * Returns a custom data object that is registered for a DOM node by id
21914      * @param {String|HTMLElement} id The DOM node or id to look up
21915      * @return {Object} data The custom data
21916      */
21917         getTarget : function(id){
21918             if(typeof id != "string"){ // must be element?
21919                 id = id.id;
21920             }
21921             return elements[id];
21922         },
21923
21924     /**
21925      * Returns a custom data object that is registered for the DOM node that is the target of the event
21926      * @param {Event} e The event
21927      * @return {Object} data The custom data
21928      */
21929         getTargetFromEvent : function(e){
21930             var t = Roo.lib.Event.getTarget(e);
21931             return t ? elements[t.id] || handles[t.id] : null;
21932         }
21933     };
21934 }();/*
21935  * Based on:
21936  * Ext JS Library 1.1.1
21937  * Copyright(c) 2006-2007, Ext JS, LLC.
21938  *
21939  * Originally Released Under LGPL - original licence link has changed is not relivant.
21940  *
21941  * Fork - LGPL
21942  * <script type="text/javascript">
21943  */
21944  
21945
21946 /**
21947  * @class Roo.dd.StatusProxy
21948  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21949  * default drag proxy used by all Roo.dd components.
21950  * @constructor
21951  * @param {Object} config
21952  */
21953 Roo.dd.StatusProxy = function(config){
21954     Roo.apply(this, config);
21955     this.id = this.id || Roo.id();
21956     this.el = new Roo.Layer({
21957         dh: {
21958             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21959                 {tag: "div", cls: "x-dd-drop-icon"},
21960                 {tag: "div", cls: "x-dd-drag-ghost"}
21961             ]
21962         }, 
21963         shadow: !config || config.shadow !== false
21964     });
21965     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21966     this.dropStatus = this.dropNotAllowed;
21967 };
21968
21969 Roo.dd.StatusProxy.prototype = {
21970     /**
21971      * @cfg {String} dropAllowed
21972      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21973      */
21974     dropAllowed : "x-dd-drop-ok",
21975     /**
21976      * @cfg {String} dropNotAllowed
21977      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21978      */
21979     dropNotAllowed : "x-dd-drop-nodrop",
21980
21981     /**
21982      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21983      * over the current target element.
21984      * @param {String} cssClass The css class for the new drop status indicator image
21985      */
21986     setStatus : function(cssClass){
21987         cssClass = cssClass || this.dropNotAllowed;
21988         if(this.dropStatus != cssClass){
21989             this.el.replaceClass(this.dropStatus, cssClass);
21990             this.dropStatus = cssClass;
21991         }
21992     },
21993
21994     /**
21995      * Resets the status indicator to the default dropNotAllowed value
21996      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21997      */
21998     reset : function(clearGhost){
21999         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22000         this.dropStatus = this.dropNotAllowed;
22001         if(clearGhost){
22002             this.ghost.update("");
22003         }
22004     },
22005
22006     /**
22007      * Updates the contents of the ghost element
22008      * @param {String} html The html that will replace the current innerHTML of the ghost element
22009      */
22010     update : function(html){
22011         if(typeof html == "string"){
22012             this.ghost.update(html);
22013         }else{
22014             this.ghost.update("");
22015             html.style.margin = "0";
22016             this.ghost.dom.appendChild(html);
22017         }
22018         // ensure float = none set?? cant remember why though.
22019         var el = this.ghost.dom.firstChild;
22020                 if(el){
22021                         Roo.fly(el).setStyle('float', 'none');
22022                 }
22023     },
22024     
22025     /**
22026      * Returns the underlying proxy {@link Roo.Layer}
22027      * @return {Roo.Layer} el
22028     */
22029     getEl : function(){
22030         return this.el;
22031     },
22032
22033     /**
22034      * Returns the ghost element
22035      * @return {Roo.Element} el
22036      */
22037     getGhost : function(){
22038         return this.ghost;
22039     },
22040
22041     /**
22042      * Hides the proxy
22043      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22044      */
22045     hide : function(clear){
22046         this.el.hide();
22047         if(clear){
22048             this.reset(true);
22049         }
22050     },
22051
22052     /**
22053      * Stops the repair animation if it's currently running
22054      */
22055     stop : function(){
22056         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22057             this.anim.stop();
22058         }
22059     },
22060
22061     /**
22062      * Displays this proxy
22063      */
22064     show : function(){
22065         this.el.show();
22066     },
22067
22068     /**
22069      * Force the Layer to sync its shadow and shim positions to the element
22070      */
22071     sync : function(){
22072         this.el.sync();
22073     },
22074
22075     /**
22076      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22077      * invalid drop operation by the item being dragged.
22078      * @param {Array} xy The XY position of the element ([x, y])
22079      * @param {Function} callback The function to call after the repair is complete
22080      * @param {Object} scope The scope in which to execute the callback
22081      */
22082     repair : function(xy, callback, scope){
22083         this.callback = callback;
22084         this.scope = scope;
22085         if(xy && this.animRepair !== false){
22086             this.el.addClass("x-dd-drag-repair");
22087             this.el.hideUnders(true);
22088             this.anim = this.el.shift({
22089                 duration: this.repairDuration || .5,
22090                 easing: 'easeOut',
22091                 xy: xy,
22092                 stopFx: true,
22093                 callback: this.afterRepair,
22094                 scope: this
22095             });
22096         }else{
22097             this.afterRepair();
22098         }
22099     },
22100
22101     // private
22102     afterRepair : function(){
22103         this.hide(true);
22104         if(typeof this.callback == "function"){
22105             this.callback.call(this.scope || this);
22106         }
22107         this.callback = null;
22108         this.scope = null;
22109     }
22110 };/*
22111  * Based on:
22112  * Ext JS Library 1.1.1
22113  * Copyright(c) 2006-2007, Ext JS, LLC.
22114  *
22115  * Originally Released Under LGPL - original licence link has changed is not relivant.
22116  *
22117  * Fork - LGPL
22118  * <script type="text/javascript">
22119  */
22120
22121 /**
22122  * @class Roo.dd.DragSource
22123  * @extends Roo.dd.DDProxy
22124  * A simple class that provides the basic implementation needed to make any element draggable.
22125  * @constructor
22126  * @param {String/HTMLElement/Element} el The container element
22127  * @param {Object} config
22128  */
22129 Roo.dd.DragSource = function(el, config){
22130     this.el = Roo.get(el);
22131     this.dragData = {};
22132     
22133     Roo.apply(this, config);
22134     
22135     if(!this.proxy){
22136         this.proxy = new Roo.dd.StatusProxy();
22137     }
22138
22139     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22140           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22141     
22142     this.dragging = false;
22143 };
22144
22145 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22146     /**
22147      * @cfg {String} dropAllowed
22148      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22149      */
22150     dropAllowed : "x-dd-drop-ok",
22151     /**
22152      * @cfg {String} dropNotAllowed
22153      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22154      */
22155     dropNotAllowed : "x-dd-drop-nodrop",
22156
22157     /**
22158      * Returns the data object associated with this drag source
22159      * @return {Object} data An object containing arbitrary data
22160      */
22161     getDragData : function(e){
22162         return this.dragData;
22163     },
22164
22165     // private
22166     onDragEnter : function(e, id){
22167         var target = Roo.dd.DragDropMgr.getDDById(id);
22168         this.cachedTarget = target;
22169         if(this.beforeDragEnter(target, e, id) !== false){
22170             if(target.isNotifyTarget){
22171                 var status = target.notifyEnter(this, e, this.dragData);
22172                 this.proxy.setStatus(status);
22173             }else{
22174                 this.proxy.setStatus(this.dropAllowed);
22175             }
22176             
22177             if(this.afterDragEnter){
22178                 /**
22179                  * An empty function by default, but provided so that you can perform a custom action
22180                  * when the dragged item enters the drop target by providing an implementation.
22181                  * @param {Roo.dd.DragDrop} target The drop target
22182                  * @param {Event} e The event object
22183                  * @param {String} id The id of the dragged element
22184                  * @method afterDragEnter
22185                  */
22186                 this.afterDragEnter(target, e, id);
22187             }
22188         }
22189     },
22190
22191     /**
22192      * An empty function by default, but provided so that you can perform a custom action
22193      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22194      * @param {Roo.dd.DragDrop} target The drop target
22195      * @param {Event} e The event object
22196      * @param {String} id The id of the dragged element
22197      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22198      */
22199     beforeDragEnter : function(target, e, id){
22200         return true;
22201     },
22202
22203     // private
22204     alignElWithMouse: function() {
22205         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22206         this.proxy.sync();
22207     },
22208
22209     // private
22210     onDragOver : function(e, id){
22211         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22212         if(this.beforeDragOver(target, e, id) !== false){
22213             if(target.isNotifyTarget){
22214                 var status = target.notifyOver(this, e, this.dragData);
22215                 this.proxy.setStatus(status);
22216             }
22217
22218             if(this.afterDragOver){
22219                 /**
22220                  * An empty function by default, but provided so that you can perform a custom action
22221                  * while the dragged item is over the drop target by providing an implementation.
22222                  * @param {Roo.dd.DragDrop} target The drop target
22223                  * @param {Event} e The event object
22224                  * @param {String} id The id of the dragged element
22225                  * @method afterDragOver
22226                  */
22227                 this.afterDragOver(target, e, id);
22228             }
22229         }
22230     },
22231
22232     /**
22233      * An empty function by default, but provided so that you can perform a custom action
22234      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22235      * @param {Roo.dd.DragDrop} target The drop target
22236      * @param {Event} e The event object
22237      * @param {String} id The id of the dragged element
22238      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22239      */
22240     beforeDragOver : function(target, e, id){
22241         return true;
22242     },
22243
22244     // private
22245     onDragOut : function(e, id){
22246         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22247         if(this.beforeDragOut(target, e, id) !== false){
22248             if(target.isNotifyTarget){
22249                 target.notifyOut(this, e, this.dragData);
22250             }
22251             this.proxy.reset();
22252             if(this.afterDragOut){
22253                 /**
22254                  * An empty function by default, but provided so that you can perform a custom action
22255                  * after the dragged item is dragged out of the target without dropping.
22256                  * @param {Roo.dd.DragDrop} target The drop target
22257                  * @param {Event} e The event object
22258                  * @param {String} id The id of the dragged element
22259                  * @method afterDragOut
22260                  */
22261                 this.afterDragOut(target, e, id);
22262             }
22263         }
22264         this.cachedTarget = null;
22265     },
22266
22267     /**
22268      * An empty function by default, but provided so that you can perform a custom action before the dragged
22269      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22270      * @param {Roo.dd.DragDrop} target The drop target
22271      * @param {Event} e The event object
22272      * @param {String} id The id of the dragged element
22273      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22274      */
22275     beforeDragOut : function(target, e, id){
22276         return true;
22277     },
22278     
22279     // private
22280     onDragDrop : function(e, id){
22281         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22282         if(this.beforeDragDrop(target, e, id) !== false){
22283             if(target.isNotifyTarget){
22284                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22285                     this.onValidDrop(target, e, id);
22286                 }else{
22287                     this.onInvalidDrop(target, e, id);
22288                 }
22289             }else{
22290                 this.onValidDrop(target, e, id);
22291             }
22292             
22293             if(this.afterDragDrop){
22294                 /**
22295                  * An empty function by default, but provided so that you can perform a custom action
22296                  * after a valid drag drop has occurred by providing an implementation.
22297                  * @param {Roo.dd.DragDrop} target The drop target
22298                  * @param {Event} e The event object
22299                  * @param {String} id The id of the dropped element
22300                  * @method afterDragDrop
22301                  */
22302                 this.afterDragDrop(target, e, id);
22303             }
22304         }
22305         delete this.cachedTarget;
22306     },
22307
22308     /**
22309      * An empty function by default, but provided so that you can perform a custom action before the dragged
22310      * item is dropped onto the target and optionally cancel the onDragDrop.
22311      * @param {Roo.dd.DragDrop} target The drop target
22312      * @param {Event} e The event object
22313      * @param {String} id The id of the dragged element
22314      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22315      */
22316     beforeDragDrop : function(target, e, id){
22317         return true;
22318     },
22319
22320     // private
22321     onValidDrop : function(target, e, id){
22322         this.hideProxy();
22323         if(this.afterValidDrop){
22324             /**
22325              * An empty function by default, but provided so that you can perform a custom action
22326              * after a valid drop has occurred by providing an implementation.
22327              * @param {Object} target The target DD 
22328              * @param {Event} e The event object
22329              * @param {String} id The id of the dropped element
22330              * @method afterInvalidDrop
22331              */
22332             this.afterValidDrop(target, e, id);
22333         }
22334     },
22335
22336     // private
22337     getRepairXY : function(e, data){
22338         return this.el.getXY();  
22339     },
22340
22341     // private
22342     onInvalidDrop : function(target, e, id){
22343         this.beforeInvalidDrop(target, e, id);
22344         if(this.cachedTarget){
22345             if(this.cachedTarget.isNotifyTarget){
22346                 this.cachedTarget.notifyOut(this, e, this.dragData);
22347             }
22348             this.cacheTarget = null;
22349         }
22350         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22351
22352         if(this.afterInvalidDrop){
22353             /**
22354              * An empty function by default, but provided so that you can perform a custom action
22355              * after an invalid drop has occurred by providing an implementation.
22356              * @param {Event} e The event object
22357              * @param {String} id The id of the dropped element
22358              * @method afterInvalidDrop
22359              */
22360             this.afterInvalidDrop(e, id);
22361         }
22362     },
22363
22364     // private
22365     afterRepair : function(){
22366         if(Roo.enableFx){
22367             this.el.highlight(this.hlColor || "c3daf9");
22368         }
22369         this.dragging = false;
22370     },
22371
22372     /**
22373      * An empty function by default, but provided so that you can perform a custom action after an invalid
22374      * drop has occurred.
22375      * @param {Roo.dd.DragDrop} target The drop target
22376      * @param {Event} e The event object
22377      * @param {String} id The id of the dragged element
22378      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22379      */
22380     beforeInvalidDrop : function(target, e, id){
22381         return true;
22382     },
22383
22384     // private
22385     handleMouseDown : function(e){
22386         if(this.dragging) {
22387             return;
22388         }
22389         var data = this.getDragData(e);
22390         if(data && this.onBeforeDrag(data, e) !== false){
22391             this.dragData = data;
22392             this.proxy.stop();
22393             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22394         } 
22395     },
22396
22397     /**
22398      * An empty function by default, but provided so that you can perform a custom action before the initial
22399      * drag event begins and optionally cancel it.
22400      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22401      * @param {Event} e The event object
22402      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22403      */
22404     onBeforeDrag : function(data, e){
22405         return true;
22406     },
22407
22408     /**
22409      * An empty function by default, but provided so that you can perform a custom action once the initial
22410      * drag event has begun.  The drag cannot be canceled from this function.
22411      * @param {Number} x The x position of the click on the dragged object
22412      * @param {Number} y The y position of the click on the dragged object
22413      */
22414     onStartDrag : Roo.emptyFn,
22415
22416     // private - YUI override
22417     startDrag : function(x, y){
22418         this.proxy.reset();
22419         this.dragging = true;
22420         this.proxy.update("");
22421         this.onInitDrag(x, y);
22422         this.proxy.show();
22423     },
22424
22425     // private
22426     onInitDrag : function(x, y){
22427         var clone = this.el.dom.cloneNode(true);
22428         clone.id = Roo.id(); // prevent duplicate ids
22429         this.proxy.update(clone);
22430         this.onStartDrag(x, y);
22431         return true;
22432     },
22433
22434     /**
22435      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22436      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22437      */
22438     getProxy : function(){
22439         return this.proxy;  
22440     },
22441
22442     /**
22443      * Hides the drag source's {@link Roo.dd.StatusProxy}
22444      */
22445     hideProxy : function(){
22446         this.proxy.hide();  
22447         this.proxy.reset(true);
22448         this.dragging = false;
22449     },
22450
22451     // private
22452     triggerCacheRefresh : function(){
22453         Roo.dd.DDM.refreshCache(this.groups);
22454     },
22455
22456     // private - override to prevent hiding
22457     b4EndDrag: function(e) {
22458     },
22459
22460     // private - override to prevent moving
22461     endDrag : function(e){
22462         this.onEndDrag(this.dragData, e);
22463     },
22464
22465     // private
22466     onEndDrag : function(data, e){
22467     },
22468     
22469     // private - pin to cursor
22470     autoOffset : function(x, y) {
22471         this.setDelta(-12, -20);
22472     }    
22473 });/*
22474  * Based on:
22475  * Ext JS Library 1.1.1
22476  * Copyright(c) 2006-2007, Ext JS, LLC.
22477  *
22478  * Originally Released Under LGPL - original licence link has changed is not relivant.
22479  *
22480  * Fork - LGPL
22481  * <script type="text/javascript">
22482  */
22483
22484
22485 /**
22486  * @class Roo.dd.DropTarget
22487  * @extends Roo.dd.DDTarget
22488  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22489  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22490  * @constructor
22491  * @param {String/HTMLElement/Element} el The container element
22492  * @param {Object} config
22493  */
22494 Roo.dd.DropTarget = function(el, config){
22495     this.el = Roo.get(el);
22496     
22497     var listeners = false; ;
22498     if (config && config.listeners) {
22499         listeners= config.listeners;
22500         delete config.listeners;
22501     }
22502     Roo.apply(this, config);
22503     
22504     if(this.containerScroll){
22505         Roo.dd.ScrollManager.register(this.el);
22506     }
22507     this.addEvents( {
22508          /**
22509          * @scope Roo.dd.DropTarget
22510          */
22511          
22512          /**
22513          * @event enter
22514          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22515          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22516          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22517          * 
22518          * IMPORTANT : it should set this.overClass and this.dropAllowed
22519          * 
22520          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22521          * @param {Event} e The event
22522          * @param {Object} data An object containing arbitrary data supplied by the drag source
22523          */
22524         "enter" : true,
22525         
22526          /**
22527          * @event over
22528          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22529          * This method will be called on every mouse movement while the drag source is over the drop target.
22530          * This default implementation simply returns the dropAllowed config value.
22531          * 
22532          * IMPORTANT : it should set this.dropAllowed
22533          * 
22534          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22535          * @param {Event} e The event
22536          * @param {Object} data An object containing arbitrary data supplied by the drag source
22537          
22538          */
22539         "over" : true,
22540         /**
22541          * @event out
22542          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22543          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22544          * overClass (if any) from the drop element.
22545          * 
22546          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22547          * @param {Event} e The event
22548          * @param {Object} data An object containing arbitrary data supplied by the drag source
22549          */
22550          "out" : true,
22551          
22552         /**
22553          * @event drop
22554          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22555          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22556          * implementation that does something to process the drop event and returns true so that the drag source's
22557          * repair action does not run.
22558          * 
22559          * IMPORTANT : it should set this.success
22560          * 
22561          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22562          * @param {Event} e The event
22563          * @param {Object} data An object containing arbitrary data supplied by the drag source
22564         */
22565          "drop" : true
22566     });
22567             
22568      
22569     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22570         this.el.dom, 
22571         this.ddGroup || this.group,
22572         {
22573             isTarget: true,
22574             listeners : listeners || {} 
22575            
22576         
22577         }
22578     );
22579
22580 };
22581
22582 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22583     /**
22584      * @cfg {String} overClass
22585      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22586      */
22587      /**
22588      * @cfg {String} ddGroup
22589      * The drag drop group to handle drop events for
22590      */
22591      
22592     /**
22593      * @cfg {String} dropAllowed
22594      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22595      */
22596     dropAllowed : "x-dd-drop-ok",
22597     /**
22598      * @cfg {String} dropNotAllowed
22599      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22600      */
22601     dropNotAllowed : "x-dd-drop-nodrop",
22602     /**
22603      * @cfg {boolean} success
22604      * set this after drop listener.. 
22605      */
22606     success : false,
22607     /**
22608      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22609      * if the drop point is valid for over/enter..
22610      */
22611     valid : false,
22612     // private
22613     isTarget : true,
22614
22615     // private
22616     isNotifyTarget : true,
22617     
22618     /**
22619      * @hide
22620      */
22621     notifyEnter : function(dd, e, data)
22622     {
22623         this.valid = true;
22624         this.fireEvent('enter', dd, e, data);
22625         if(this.overClass){
22626             this.el.addClass(this.overClass);
22627         }
22628         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22629             this.valid ? this.dropAllowed : this.dropNotAllowed
22630         );
22631     },
22632
22633     /**
22634      * @hide
22635      */
22636     notifyOver : function(dd, e, data)
22637     {
22638         this.valid = true;
22639         this.fireEvent('over', dd, e, data);
22640         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22641             this.valid ? this.dropAllowed : this.dropNotAllowed
22642         );
22643     },
22644
22645     /**
22646      * @hide
22647      */
22648     notifyOut : function(dd, e, data)
22649     {
22650         this.fireEvent('out', dd, e, data);
22651         if(this.overClass){
22652             this.el.removeClass(this.overClass);
22653         }
22654     },
22655
22656     /**
22657      * @hide
22658      */
22659     notifyDrop : function(dd, e, data)
22660     {
22661         this.success = false;
22662         this.fireEvent('drop', dd, e, data);
22663         return this.success;
22664     }
22665 });/*
22666  * Based on:
22667  * Ext JS Library 1.1.1
22668  * Copyright(c) 2006-2007, Ext JS, LLC.
22669  *
22670  * Originally Released Under LGPL - original licence link has changed is not relivant.
22671  *
22672  * Fork - LGPL
22673  * <script type="text/javascript">
22674  */
22675
22676
22677 /**
22678  * @class Roo.dd.DragZone
22679  * @extends Roo.dd.DragSource
22680  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22681  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22682  * @constructor
22683  * @param {String/HTMLElement/Element} el The container element
22684  * @param {Object} config
22685  */
22686 Roo.dd.DragZone = function(el, config){
22687     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22688     if(this.containerScroll){
22689         Roo.dd.ScrollManager.register(this.el);
22690     }
22691 };
22692
22693 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22694     /**
22695      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22696      * for auto scrolling during drag operations.
22697      */
22698     /**
22699      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22700      * method after a failed drop (defaults to "c3daf9" - light blue)
22701      */
22702
22703     /**
22704      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22705      * for a valid target to drag based on the mouse down. Override this method
22706      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22707      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22708      * @param {EventObject} e The mouse down event
22709      * @return {Object} The dragData
22710      */
22711     getDragData : function(e){
22712         return Roo.dd.Registry.getHandleFromEvent(e);
22713     },
22714     
22715     /**
22716      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22717      * this.dragData.ddel
22718      * @param {Number} x The x position of the click on the dragged object
22719      * @param {Number} y The y position of the click on the dragged object
22720      * @return {Boolean} true to continue the drag, false to cancel
22721      */
22722     onInitDrag : function(x, y){
22723         this.proxy.update(this.dragData.ddel.cloneNode(true));
22724         this.onStartDrag(x, y);
22725         return true;
22726     },
22727     
22728     /**
22729      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22730      */
22731     afterRepair : function(){
22732         if(Roo.enableFx){
22733             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22734         }
22735         this.dragging = false;
22736     },
22737
22738     /**
22739      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22740      * the XY of this.dragData.ddel
22741      * @param {EventObject} e The mouse up event
22742      * @return {Array} The xy location (e.g. [100, 200])
22743      */
22744     getRepairXY : function(e){
22745         return Roo.Element.fly(this.dragData.ddel).getXY();  
22746     }
22747 });/*
22748  * Based on:
22749  * Ext JS Library 1.1.1
22750  * Copyright(c) 2006-2007, Ext JS, LLC.
22751  *
22752  * Originally Released Under LGPL - original licence link has changed is not relivant.
22753  *
22754  * Fork - LGPL
22755  * <script type="text/javascript">
22756  */
22757 /**
22758  * @class Roo.dd.DropZone
22759  * @extends Roo.dd.DropTarget
22760  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22761  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22762  * @constructor
22763  * @param {String/HTMLElement/Element} el The container element
22764  * @param {Object} config
22765  */
22766 Roo.dd.DropZone = function(el, config){
22767     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22768 };
22769
22770 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22771     /**
22772      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22773      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22774      * provide your own custom lookup.
22775      * @param {Event} e The event
22776      * @return {Object} data The custom data
22777      */
22778     getTargetFromEvent : function(e){
22779         return Roo.dd.Registry.getTargetFromEvent(e);
22780     },
22781
22782     /**
22783      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22784      * that it has registered.  This method has no default implementation and should be overridden to provide
22785      * node-specific processing if necessary.
22786      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22787      * {@link #getTargetFromEvent} for this node)
22788      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22789      * @param {Event} e The event
22790      * @param {Object} data An object containing arbitrary data supplied by the drag source
22791      */
22792     onNodeEnter : function(n, dd, e, data){
22793         
22794     },
22795
22796     /**
22797      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22798      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22799      * overridden to provide the proper feedback.
22800      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22801      * {@link #getTargetFromEvent} for this node)
22802      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22803      * @param {Event} e The event
22804      * @param {Object} data An object containing arbitrary data supplied by the drag source
22805      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22806      * underlying {@link Roo.dd.StatusProxy} can be updated
22807      */
22808     onNodeOver : function(n, dd, e, data){
22809         return this.dropAllowed;
22810     },
22811
22812     /**
22813      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22814      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22815      * node-specific processing if necessary.
22816      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22817      * {@link #getTargetFromEvent} for this node)
22818      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22819      * @param {Event} e The event
22820      * @param {Object} data An object containing arbitrary data supplied by the drag source
22821      */
22822     onNodeOut : function(n, dd, e, data){
22823         
22824     },
22825
22826     /**
22827      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22828      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22829      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22830      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22831      * {@link #getTargetFromEvent} for this node)
22832      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22833      * @param {Event} e The event
22834      * @param {Object} data An object containing arbitrary data supplied by the drag source
22835      * @return {Boolean} True if the drop was valid, else false
22836      */
22837     onNodeDrop : function(n, dd, e, data){
22838         return false;
22839     },
22840
22841     /**
22842      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22843      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22844      * it should be overridden to provide the proper feedback if necessary.
22845      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22846      * @param {Event} e The event
22847      * @param {Object} data An object containing arbitrary data supplied by the drag source
22848      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22849      * underlying {@link Roo.dd.StatusProxy} can be updated
22850      */
22851     onContainerOver : function(dd, e, data){
22852         return this.dropNotAllowed;
22853     },
22854
22855     /**
22856      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22857      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22858      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22859      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22860      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22861      * @param {Event} e The event
22862      * @param {Object} data An object containing arbitrary data supplied by the drag source
22863      * @return {Boolean} True if the drop was valid, else false
22864      */
22865     onContainerDrop : function(dd, e, data){
22866         return false;
22867     },
22868
22869     /**
22870      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22871      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22872      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22873      * you should override this method and provide a custom implementation.
22874      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22875      * @param {Event} e The event
22876      * @param {Object} data An object containing arbitrary data supplied by the drag source
22877      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22878      * underlying {@link Roo.dd.StatusProxy} can be updated
22879      */
22880     notifyEnter : function(dd, e, data){
22881         return this.dropNotAllowed;
22882     },
22883
22884     /**
22885      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22886      * This method will be called on every mouse movement while the drag source is over the drop zone.
22887      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22888      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22889      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22890      * registered node, it will call {@link #onContainerOver}.
22891      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22892      * @param {Event} e The event
22893      * @param {Object} data An object containing arbitrary data supplied by the drag source
22894      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22895      * underlying {@link Roo.dd.StatusProxy} can be updated
22896      */
22897     notifyOver : function(dd, e, data){
22898         var n = this.getTargetFromEvent(e);
22899         if(!n){ // not over valid drop target
22900             if(this.lastOverNode){
22901                 this.onNodeOut(this.lastOverNode, dd, e, data);
22902                 this.lastOverNode = null;
22903             }
22904             return this.onContainerOver(dd, e, data);
22905         }
22906         if(this.lastOverNode != n){
22907             if(this.lastOverNode){
22908                 this.onNodeOut(this.lastOverNode, dd, e, data);
22909             }
22910             this.onNodeEnter(n, dd, e, data);
22911             this.lastOverNode = n;
22912         }
22913         return this.onNodeOver(n, dd, e, data);
22914     },
22915
22916     /**
22917      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22918      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22919      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22920      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22921      * @param {Event} e The event
22922      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22923      */
22924     notifyOut : function(dd, e, data){
22925         if(this.lastOverNode){
22926             this.onNodeOut(this.lastOverNode, dd, e, data);
22927             this.lastOverNode = null;
22928         }
22929     },
22930
22931     /**
22932      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22933      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22934      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22935      * otherwise it will call {@link #onContainerDrop}.
22936      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22937      * @param {Event} e The event
22938      * @param {Object} data An object containing arbitrary data supplied by the drag source
22939      * @return {Boolean} True if the drop was valid, else false
22940      */
22941     notifyDrop : function(dd, e, data){
22942         if(this.lastOverNode){
22943             this.onNodeOut(this.lastOverNode, dd, e, data);
22944             this.lastOverNode = null;
22945         }
22946         var n = this.getTargetFromEvent(e);
22947         return n ?
22948             this.onNodeDrop(n, dd, e, data) :
22949             this.onContainerDrop(dd, e, data);
22950     },
22951
22952     // private
22953     triggerCacheRefresh : function(){
22954         Roo.dd.DDM.refreshCache(this.groups);
22955     }  
22956 });/*
22957  * Based on:
22958  * Ext JS Library 1.1.1
22959  * Copyright(c) 2006-2007, Ext JS, LLC.
22960  *
22961  * Originally Released Under LGPL - original licence link has changed is not relivant.
22962  *
22963  * Fork - LGPL
22964  * <script type="text/javascript">
22965  */
22966
22967
22968 /**
22969  * @class Roo.data.SortTypes
22970  * @singleton
22971  * Defines the default sorting (casting?) comparison functions used when sorting data.
22972  */
22973 Roo.data.SortTypes = {
22974     /**
22975      * Default sort that does nothing
22976      * @param {Mixed} s The value being converted
22977      * @return {Mixed} The comparison value
22978      */
22979     none : function(s){
22980         return s;
22981     },
22982     
22983     /**
22984      * The regular expression used to strip tags
22985      * @type {RegExp}
22986      * @property
22987      */
22988     stripTagsRE : /<\/?[^>]+>/gi,
22989     
22990     /**
22991      * Strips all HTML tags to sort on text only
22992      * @param {Mixed} s The value being converted
22993      * @return {String} The comparison value
22994      */
22995     asText : function(s){
22996         return String(s).replace(this.stripTagsRE, "");
22997     },
22998     
22999     /**
23000      * Strips all HTML tags to sort on text only - Case insensitive
23001      * @param {Mixed} s The value being converted
23002      * @return {String} The comparison value
23003      */
23004     asUCText : function(s){
23005         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23006     },
23007     
23008     /**
23009      * Case insensitive string
23010      * @param {Mixed} s The value being converted
23011      * @return {String} The comparison value
23012      */
23013     asUCString : function(s) {
23014         return String(s).toUpperCase();
23015     },
23016     
23017     /**
23018      * Date sorting
23019      * @param {Mixed} s The value being converted
23020      * @return {Number} The comparison value
23021      */
23022     asDate : function(s) {
23023         if(!s){
23024             return 0;
23025         }
23026         if(s instanceof Date){
23027             return s.getTime();
23028         }
23029         return Date.parse(String(s));
23030     },
23031     
23032     /**
23033      * Float sorting
23034      * @param {Mixed} s The value being converted
23035      * @return {Float} The comparison value
23036      */
23037     asFloat : function(s) {
23038         var val = parseFloat(String(s).replace(/,/g, ""));
23039         if(isNaN(val)) {
23040             val = 0;
23041         }
23042         return val;
23043     },
23044     
23045     /**
23046      * Integer sorting
23047      * @param {Mixed} s The value being converted
23048      * @return {Number} The comparison value
23049      */
23050     asInt : function(s) {
23051         var val = parseInt(String(s).replace(/,/g, ""));
23052         if(isNaN(val)) {
23053             val = 0;
23054         }
23055         return val;
23056     }
23057 };/*
23058  * Based on:
23059  * Ext JS Library 1.1.1
23060  * Copyright(c) 2006-2007, Ext JS, LLC.
23061  *
23062  * Originally Released Under LGPL - original licence link has changed is not relivant.
23063  *
23064  * Fork - LGPL
23065  * <script type="text/javascript">
23066  */
23067
23068 /**
23069 * @class Roo.data.Record
23070  * Instances of this class encapsulate both record <em>definition</em> information, and record
23071  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23072  * to access Records cached in an {@link Roo.data.Store} object.<br>
23073  * <p>
23074  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23075  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23076  * objects.<br>
23077  * <p>
23078  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23079  * @constructor
23080  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23081  * {@link #create}. The parameters are the same.
23082  * @param {Array} data An associative Array of data values keyed by the field name.
23083  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23084  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23085  * not specified an integer id is generated.
23086  */
23087 Roo.data.Record = function(data, id){
23088     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23089     this.data = data;
23090 };
23091
23092 /**
23093  * Generate a constructor for a specific record layout.
23094  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23095  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23096  * Each field definition object may contain the following properties: <ul>
23097  * <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,
23098  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23099  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23100  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23101  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23102  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23103  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23104  * this may be omitted.</p></li>
23105  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23106  * <ul><li>auto (Default, implies no conversion)</li>
23107  * <li>string</li>
23108  * <li>int</li>
23109  * <li>float</li>
23110  * <li>boolean</li>
23111  * <li>date</li></ul></p></li>
23112  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23113  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23114  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23115  * by the Reader into an object that will be stored in the Record. It is passed the
23116  * following parameters:<ul>
23117  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23118  * </ul></p></li>
23119  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23120  * </ul>
23121  * <br>usage:<br><pre><code>
23122 var TopicRecord = Roo.data.Record.create(
23123     {name: 'title', mapping: 'topic_title'},
23124     {name: 'author', mapping: 'username'},
23125     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23126     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23127     {name: 'lastPoster', mapping: 'user2'},
23128     {name: 'excerpt', mapping: 'post_text'}
23129 );
23130
23131 var myNewRecord = new TopicRecord({
23132     title: 'Do my job please',
23133     author: 'noobie',
23134     totalPosts: 1,
23135     lastPost: new Date(),
23136     lastPoster: 'Animal',
23137     excerpt: 'No way dude!'
23138 });
23139 myStore.add(myNewRecord);
23140 </code></pre>
23141  * @method create
23142  * @static
23143  */
23144 Roo.data.Record.create = function(o){
23145     var f = function(){
23146         f.superclass.constructor.apply(this, arguments);
23147     };
23148     Roo.extend(f, Roo.data.Record);
23149     var p = f.prototype;
23150     p.fields = new Roo.util.MixedCollection(false, function(field){
23151         return field.name;
23152     });
23153     for(var i = 0, len = o.length; i < len; i++){
23154         p.fields.add(new Roo.data.Field(o[i]));
23155     }
23156     f.getField = function(name){
23157         return p.fields.get(name);  
23158     };
23159     return f;
23160 };
23161
23162 Roo.data.Record.AUTO_ID = 1000;
23163 Roo.data.Record.EDIT = 'edit';
23164 Roo.data.Record.REJECT = 'reject';
23165 Roo.data.Record.COMMIT = 'commit';
23166
23167 Roo.data.Record.prototype = {
23168     /**
23169      * Readonly flag - true if this record has been modified.
23170      * @type Boolean
23171      */
23172     dirty : false,
23173     editing : false,
23174     error: null,
23175     modified: null,
23176
23177     // private
23178     join : function(store){
23179         this.store = store;
23180     },
23181
23182     /**
23183      * Set the named field to the specified value.
23184      * @param {String} name The name of the field to set.
23185      * @param {Object} value The value to set the field to.
23186      */
23187     set : function(name, value){
23188         if(this.data[name] == value){
23189             return;
23190         }
23191         this.dirty = true;
23192         if(!this.modified){
23193             this.modified = {};
23194         }
23195         if(typeof this.modified[name] == 'undefined'){
23196             this.modified[name] = this.data[name];
23197         }
23198         this.data[name] = value;
23199         if(!this.editing && this.store){
23200             this.store.afterEdit(this);
23201         }       
23202     },
23203
23204     /**
23205      * Get the value of the named field.
23206      * @param {String} name The name of the field to get the value of.
23207      * @return {Object} The value of the field.
23208      */
23209     get : function(name){
23210         return this.data[name]; 
23211     },
23212
23213     // private
23214     beginEdit : function(){
23215         this.editing = true;
23216         this.modified = {}; 
23217     },
23218
23219     // private
23220     cancelEdit : function(){
23221         this.editing = false;
23222         delete this.modified;
23223     },
23224
23225     // private
23226     endEdit : function(){
23227         this.editing = false;
23228         if(this.dirty && this.store){
23229             this.store.afterEdit(this);
23230         }
23231     },
23232
23233     /**
23234      * Usually called by the {@link Roo.data.Store} which owns the Record.
23235      * Rejects all changes made to the Record since either creation, or the last commit operation.
23236      * Modified fields are reverted to their original values.
23237      * <p>
23238      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23239      * of reject operations.
23240      */
23241     reject : function(){
23242         var m = this.modified;
23243         for(var n in m){
23244             if(typeof m[n] != "function"){
23245                 this.data[n] = m[n];
23246             }
23247         }
23248         this.dirty = false;
23249         delete this.modified;
23250         this.editing = false;
23251         if(this.store){
23252             this.store.afterReject(this);
23253         }
23254     },
23255
23256     /**
23257      * Usually called by the {@link Roo.data.Store} which owns the Record.
23258      * Commits all changes made to the Record since either creation, or the last commit operation.
23259      * <p>
23260      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23261      * of commit operations.
23262      */
23263     commit : function(){
23264         this.dirty = false;
23265         delete this.modified;
23266         this.editing = false;
23267         if(this.store){
23268             this.store.afterCommit(this);
23269         }
23270     },
23271
23272     // private
23273     hasError : function(){
23274         return this.error != null;
23275     },
23276
23277     // private
23278     clearError : function(){
23279         this.error = null;
23280     },
23281
23282     /**
23283      * Creates a copy of this record.
23284      * @param {String} id (optional) A new record id if you don't want to use this record's id
23285      * @return {Record}
23286      */
23287     copy : function(newId) {
23288         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23289     }
23290 };/*
23291  * Based on:
23292  * Ext JS Library 1.1.1
23293  * Copyright(c) 2006-2007, Ext JS, LLC.
23294  *
23295  * Originally Released Under LGPL - original licence link has changed is not relivant.
23296  *
23297  * Fork - LGPL
23298  * <script type="text/javascript">
23299  */
23300
23301
23302
23303 /**
23304  * @class Roo.data.Store
23305  * @extends Roo.util.Observable
23306  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23307  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23308  * <p>
23309  * 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
23310  * has no knowledge of the format of the data returned by the Proxy.<br>
23311  * <p>
23312  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23313  * instances from the data object. These records are cached and made available through accessor functions.
23314  * @constructor
23315  * Creates a new Store.
23316  * @param {Object} config A config object containing the objects needed for the Store to access data,
23317  * and read the data into Records.
23318  */
23319 Roo.data.Store = function(config){
23320     this.data = new Roo.util.MixedCollection(false);
23321     this.data.getKey = function(o){
23322         return o.id;
23323     };
23324     this.baseParams = {};
23325     // private
23326     this.paramNames = {
23327         "start" : "start",
23328         "limit" : "limit",
23329         "sort" : "sort",
23330         "dir" : "dir",
23331         "multisort" : "_multisort"
23332     };
23333
23334     if(config && config.data){
23335         this.inlineData = config.data;
23336         delete config.data;
23337     }
23338
23339     Roo.apply(this, config);
23340     
23341     if(this.reader){ // reader passed
23342         this.reader = Roo.factory(this.reader, Roo.data);
23343         this.reader.xmodule = this.xmodule || false;
23344         if(!this.recordType){
23345             this.recordType = this.reader.recordType;
23346         }
23347         if(this.reader.onMetaChange){
23348             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23349         }
23350     }
23351
23352     if(this.recordType){
23353         this.fields = this.recordType.prototype.fields;
23354     }
23355     this.modified = [];
23356
23357     this.addEvents({
23358         /**
23359          * @event datachanged
23360          * Fires when the data cache has changed, and a widget which is using this Store
23361          * as a Record cache should refresh its view.
23362          * @param {Store} this
23363          */
23364         datachanged : true,
23365         /**
23366          * @event metachange
23367          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23368          * @param {Store} this
23369          * @param {Object} meta The JSON metadata
23370          */
23371         metachange : true,
23372         /**
23373          * @event add
23374          * Fires when Records have been added to the Store
23375          * @param {Store} this
23376          * @param {Roo.data.Record[]} records The array of Records added
23377          * @param {Number} index The index at which the record(s) were added
23378          */
23379         add : true,
23380         /**
23381          * @event remove
23382          * Fires when a Record has been removed from the Store
23383          * @param {Store} this
23384          * @param {Roo.data.Record} record The Record that was removed
23385          * @param {Number} index The index at which the record was removed
23386          */
23387         remove : true,
23388         /**
23389          * @event update
23390          * Fires when a Record has been updated
23391          * @param {Store} this
23392          * @param {Roo.data.Record} record The Record that was updated
23393          * @param {String} operation The update operation being performed.  Value may be one of:
23394          * <pre><code>
23395  Roo.data.Record.EDIT
23396  Roo.data.Record.REJECT
23397  Roo.data.Record.COMMIT
23398          * </code></pre>
23399          */
23400         update : true,
23401         /**
23402          * @event clear
23403          * Fires when the data cache has been cleared.
23404          * @param {Store} this
23405          */
23406         clear : true,
23407         /**
23408          * @event beforeload
23409          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23410          * the load action will be canceled.
23411          * @param {Store} this
23412          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23413          */
23414         beforeload : true,
23415         /**
23416          * @event beforeloadadd
23417          * Fires after a new set of Records has been loaded.
23418          * @param {Store} this
23419          * @param {Roo.data.Record[]} records The Records that were loaded
23420          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23421          */
23422         beforeloadadd : true,
23423         /**
23424          * @event load
23425          * Fires after a new set of Records has been loaded, before they are added to the store.
23426          * @param {Store} this
23427          * @param {Roo.data.Record[]} records The Records that were loaded
23428          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23429          * @params {Object} return from reader
23430          */
23431         load : true,
23432         /**
23433          * @event loadexception
23434          * Fires if an exception occurs in the Proxy during loading.
23435          * Called with the signature of the Proxy's "loadexception" event.
23436          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23437          * 
23438          * @param {Proxy} 
23439          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23440          * @param {Object} load options 
23441          * @param {Object} jsonData from your request (normally this contains the Exception)
23442          */
23443         loadexception : true
23444     });
23445     
23446     if(this.proxy){
23447         this.proxy = Roo.factory(this.proxy, Roo.data);
23448         this.proxy.xmodule = this.xmodule || false;
23449         this.relayEvents(this.proxy,  ["loadexception"]);
23450     }
23451     this.sortToggle = {};
23452     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23453
23454     Roo.data.Store.superclass.constructor.call(this);
23455
23456     if(this.inlineData){
23457         this.loadData(this.inlineData);
23458         delete this.inlineData;
23459     }
23460 };
23461
23462 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23463      /**
23464     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23465     * without a remote query - used by combo/forms at present.
23466     */
23467     
23468     /**
23469     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23470     */
23471     /**
23472     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23473     */
23474     /**
23475     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23476     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23477     */
23478     /**
23479     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23480     * on any HTTP request
23481     */
23482     /**
23483     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23484     */
23485     /**
23486     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23487     */
23488     multiSort: false,
23489     /**
23490     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23491     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23492     */
23493     remoteSort : false,
23494
23495     /**
23496     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23497      * loaded or when a record is removed. (defaults to false).
23498     */
23499     pruneModifiedRecords : false,
23500
23501     // private
23502     lastOptions : null,
23503
23504     /**
23505      * Add Records to the Store and fires the add event.
23506      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23507      */
23508     add : function(records){
23509         records = [].concat(records);
23510         for(var i = 0, len = records.length; i < len; i++){
23511             records[i].join(this);
23512         }
23513         var index = this.data.length;
23514         this.data.addAll(records);
23515         this.fireEvent("add", this, records, index);
23516     },
23517
23518     /**
23519      * Remove a Record from the Store and fires the remove event.
23520      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23521      */
23522     remove : function(record){
23523         var index = this.data.indexOf(record);
23524         this.data.removeAt(index);
23525  
23526         if(this.pruneModifiedRecords){
23527             this.modified.remove(record);
23528         }
23529         this.fireEvent("remove", this, record, index);
23530     },
23531
23532     /**
23533      * Remove all Records from the Store and fires the clear event.
23534      */
23535     removeAll : function(){
23536         this.data.clear();
23537         if(this.pruneModifiedRecords){
23538             this.modified = [];
23539         }
23540         this.fireEvent("clear", this);
23541     },
23542
23543     /**
23544      * Inserts Records to the Store at the given index and fires the add event.
23545      * @param {Number} index The start index at which to insert the passed Records.
23546      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23547      */
23548     insert : function(index, records){
23549         records = [].concat(records);
23550         for(var i = 0, len = records.length; i < len; i++){
23551             this.data.insert(index, records[i]);
23552             records[i].join(this);
23553         }
23554         this.fireEvent("add", this, records, index);
23555     },
23556
23557     /**
23558      * Get the index within the cache of the passed Record.
23559      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23560      * @return {Number} The index of the passed Record. Returns -1 if not found.
23561      */
23562     indexOf : function(record){
23563         return this.data.indexOf(record);
23564     },
23565
23566     /**
23567      * Get the index within the cache of the Record with the passed id.
23568      * @param {String} id The id of the Record to find.
23569      * @return {Number} The index of the Record. Returns -1 if not found.
23570      */
23571     indexOfId : function(id){
23572         return this.data.indexOfKey(id);
23573     },
23574
23575     /**
23576      * Get the Record with the specified id.
23577      * @param {String} id The id of the Record to find.
23578      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23579      */
23580     getById : function(id){
23581         return this.data.key(id);
23582     },
23583
23584     /**
23585      * Get the Record at the specified index.
23586      * @param {Number} index The index of the Record to find.
23587      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23588      */
23589     getAt : function(index){
23590         return this.data.itemAt(index);
23591     },
23592
23593     /**
23594      * Returns a range of Records between specified indices.
23595      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23596      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23597      * @return {Roo.data.Record[]} An array of Records
23598      */
23599     getRange : function(start, end){
23600         return this.data.getRange(start, end);
23601     },
23602
23603     // private
23604     storeOptions : function(o){
23605         o = Roo.apply({}, o);
23606         delete o.callback;
23607         delete o.scope;
23608         this.lastOptions = o;
23609     },
23610
23611     /**
23612      * Loads the Record cache from the configured Proxy using the configured Reader.
23613      * <p>
23614      * If using remote paging, then the first load call must specify the <em>start</em>
23615      * and <em>limit</em> properties in the options.params property to establish the initial
23616      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23617      * <p>
23618      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23619      * and this call will return before the new data has been loaded. Perform any post-processing
23620      * in a callback function, or in a "load" event handler.</strong>
23621      * <p>
23622      * @param {Object} options An object containing properties which control loading options:<ul>
23623      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23624      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23625      * passed the following arguments:<ul>
23626      * <li>r : Roo.data.Record[]</li>
23627      * <li>options: Options object from the load call</li>
23628      * <li>success: Boolean success indicator</li></ul></li>
23629      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23630      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23631      * </ul>
23632      */
23633     load : function(options){
23634         options = options || {};
23635         if(this.fireEvent("beforeload", this, options) !== false){
23636             this.storeOptions(options);
23637             var p = Roo.apply(options.params || {}, this.baseParams);
23638             // if meta was not loaded from remote source.. try requesting it.
23639             if (!this.reader.metaFromRemote) {
23640                 p._requestMeta = 1;
23641             }
23642             if(this.sortInfo && this.remoteSort){
23643                 var pn = this.paramNames;
23644                 p[pn["sort"]] = this.sortInfo.field;
23645                 p[pn["dir"]] = this.sortInfo.direction;
23646             }
23647             if (this.multiSort) {
23648                 var pn = this.paramNames;
23649                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23650             }
23651             
23652             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23653         }
23654     },
23655
23656     /**
23657      * Reloads the Record cache from the configured Proxy using the configured Reader and
23658      * the options from the last load operation performed.
23659      * @param {Object} options (optional) An object containing properties which may override the options
23660      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23661      * the most recently used options are reused).
23662      */
23663     reload : function(options){
23664         this.load(Roo.applyIf(options||{}, this.lastOptions));
23665     },
23666
23667     // private
23668     // Called as a callback by the Reader during a load operation.
23669     loadRecords : function(o, options, success){
23670         if(!o || success === false){
23671             if(success !== false){
23672                 this.fireEvent("load", this, [], options, o);
23673             }
23674             if(options.callback){
23675                 options.callback.call(options.scope || this, [], options, false);
23676             }
23677             return;
23678         }
23679         // if data returned failure - throw an exception.
23680         if (o.success === false) {
23681             // show a message if no listener is registered.
23682             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23683                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23684             }
23685             // loadmask wil be hooked into this..
23686             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23687             return;
23688         }
23689         var r = o.records, t = o.totalRecords || r.length;
23690         
23691         this.fireEvent("beforeloadadd", this, r, options, o);
23692         
23693         if(!options || options.add !== true){
23694             if(this.pruneModifiedRecords){
23695                 this.modified = [];
23696             }
23697             for(var i = 0, len = r.length; i < len; i++){
23698                 r[i].join(this);
23699             }
23700             if(this.snapshot){
23701                 this.data = this.snapshot;
23702                 delete this.snapshot;
23703             }
23704             this.data.clear();
23705             this.data.addAll(r);
23706             this.totalLength = t;
23707             this.applySort();
23708             this.fireEvent("datachanged", this);
23709         }else{
23710             this.totalLength = Math.max(t, this.data.length+r.length);
23711             this.add(r);
23712         }
23713         
23714         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23715                 
23716             var e = new Roo.data.Record({});
23717
23718             e.set(this.parent.displayField, this.parent.emptyTitle);
23719             e.set(this.parent.valueField, '');
23720
23721             this.insert(0, e);
23722         }
23723             
23724         this.fireEvent("load", this, r, options, o);
23725         if(options.callback){
23726             options.callback.call(options.scope || this, r, options, true);
23727         }
23728     },
23729
23730
23731     /**
23732      * Loads data from a passed data block. A Reader which understands the format of the data
23733      * must have been configured in the constructor.
23734      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23735      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23736      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23737      */
23738     loadData : function(o, append){
23739         var r = this.reader.readRecords(o);
23740         this.loadRecords(r, {add: append}, true);
23741     },
23742     
23743      /**
23744      * using 'cn' the nested child reader read the child array into it's child stores.
23745      * @param {Object} rec The record with a 'children array
23746      */
23747     loadDataFromChildren : function(rec)
23748     {
23749         this.loadData(this.reader.toLoadData(rec));
23750     },
23751     
23752
23753     /**
23754      * Gets the number of cached records.
23755      * <p>
23756      * <em>If using paging, this may not be the total size of the dataset. If the data object
23757      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23758      * the data set size</em>
23759      */
23760     getCount : function(){
23761         return this.data.length || 0;
23762     },
23763
23764     /**
23765      * Gets the total number of records in the dataset as returned by the server.
23766      * <p>
23767      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23768      * the dataset size</em>
23769      */
23770     getTotalCount : function(){
23771         return this.totalLength || 0;
23772     },
23773
23774     /**
23775      * Returns the sort state of the Store as an object with two properties:
23776      * <pre><code>
23777  field {String} The name of the field by which the Records are sorted
23778  direction {String} The sort order, "ASC" or "DESC"
23779      * </code></pre>
23780      */
23781     getSortState : function(){
23782         return this.sortInfo;
23783     },
23784
23785     // private
23786     applySort : function(){
23787         if(this.sortInfo && !this.remoteSort){
23788             var s = this.sortInfo, f = s.field;
23789             var st = this.fields.get(f).sortType;
23790             var fn = function(r1, r2){
23791                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23792                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23793             };
23794             this.data.sort(s.direction, fn);
23795             if(this.snapshot && this.snapshot != this.data){
23796                 this.snapshot.sort(s.direction, fn);
23797             }
23798         }
23799     },
23800
23801     /**
23802      * Sets the default sort column and order to be used by the next load operation.
23803      * @param {String} fieldName The name of the field to sort by.
23804      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23805      */
23806     setDefaultSort : function(field, dir){
23807         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23808     },
23809
23810     /**
23811      * Sort the Records.
23812      * If remote sorting is used, the sort is performed on the server, and the cache is
23813      * reloaded. If local sorting is used, the cache is sorted internally.
23814      * @param {String} fieldName The name of the field to sort by.
23815      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23816      */
23817     sort : function(fieldName, dir){
23818         var f = this.fields.get(fieldName);
23819         if(!dir){
23820             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23821             
23822             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23823                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23824             }else{
23825                 dir = f.sortDir;
23826             }
23827         }
23828         this.sortToggle[f.name] = dir;
23829         this.sortInfo = {field: f.name, direction: dir};
23830         if(!this.remoteSort){
23831             this.applySort();
23832             this.fireEvent("datachanged", this);
23833         }else{
23834             this.load(this.lastOptions);
23835         }
23836     },
23837
23838     /**
23839      * Calls the specified function for each of the Records in the cache.
23840      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23841      * Returning <em>false</em> aborts and exits the iteration.
23842      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23843      */
23844     each : function(fn, scope){
23845         this.data.each(fn, scope);
23846     },
23847
23848     /**
23849      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23850      * (e.g., during paging).
23851      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23852      */
23853     getModifiedRecords : function(){
23854         return this.modified;
23855     },
23856
23857     // private
23858     createFilterFn : function(property, value, anyMatch){
23859         if(!value.exec){ // not a regex
23860             value = String(value);
23861             if(value.length == 0){
23862                 return false;
23863             }
23864             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23865         }
23866         return function(r){
23867             return value.test(r.data[property]);
23868         };
23869     },
23870
23871     /**
23872      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23873      * @param {String} property A field on your records
23874      * @param {Number} start The record index to start at (defaults to 0)
23875      * @param {Number} end The last record index to include (defaults to length - 1)
23876      * @return {Number} The sum
23877      */
23878     sum : function(property, start, end){
23879         var rs = this.data.items, v = 0;
23880         start = start || 0;
23881         end = (end || end === 0) ? end : rs.length-1;
23882
23883         for(var i = start; i <= end; i++){
23884             v += (rs[i].data[property] || 0);
23885         }
23886         return v;
23887     },
23888
23889     /**
23890      * Filter the records by a specified property.
23891      * @param {String} field A field on your records
23892      * @param {String/RegExp} value Either a string that the field
23893      * should start with or a RegExp to test against the field
23894      * @param {Boolean} anyMatch True to match any part not just the beginning
23895      */
23896     filter : function(property, value, anyMatch){
23897         var fn = this.createFilterFn(property, value, anyMatch);
23898         return fn ? this.filterBy(fn) : this.clearFilter();
23899     },
23900
23901     /**
23902      * Filter by a function. The specified function will be called with each
23903      * record in this data source. If the function returns true the record is included,
23904      * otherwise it is filtered.
23905      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23906      * @param {Object} scope (optional) The scope of the function (defaults to this)
23907      */
23908     filterBy : function(fn, scope){
23909         this.snapshot = this.snapshot || this.data;
23910         this.data = this.queryBy(fn, scope||this);
23911         this.fireEvent("datachanged", this);
23912     },
23913
23914     /**
23915      * Query the records by a specified property.
23916      * @param {String} field A field on your records
23917      * @param {String/RegExp} value Either a string that the field
23918      * should start with or a RegExp to test against the field
23919      * @param {Boolean} anyMatch True to match any part not just the beginning
23920      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23921      */
23922     query : function(property, value, anyMatch){
23923         var fn = this.createFilterFn(property, value, anyMatch);
23924         return fn ? this.queryBy(fn) : this.data.clone();
23925     },
23926
23927     /**
23928      * Query by a function. The specified function will be called with each
23929      * record in this data source. If the function returns true the record is included
23930      * in the results.
23931      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23932      * @param {Object} scope (optional) The scope of the function (defaults to this)
23933       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23934      **/
23935     queryBy : function(fn, scope){
23936         var data = this.snapshot || this.data;
23937         return data.filterBy(fn, scope||this);
23938     },
23939
23940     /**
23941      * Collects unique values for a particular dataIndex from this store.
23942      * @param {String} dataIndex The property to collect
23943      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23944      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23945      * @return {Array} An array of the unique values
23946      **/
23947     collect : function(dataIndex, allowNull, bypassFilter){
23948         var d = (bypassFilter === true && this.snapshot) ?
23949                 this.snapshot.items : this.data.items;
23950         var v, sv, r = [], l = {};
23951         for(var i = 0, len = d.length; i < len; i++){
23952             v = d[i].data[dataIndex];
23953             sv = String(v);
23954             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23955                 l[sv] = true;
23956                 r[r.length] = v;
23957             }
23958         }
23959         return r;
23960     },
23961
23962     /**
23963      * Revert to a view of the Record cache with no filtering applied.
23964      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23965      */
23966     clearFilter : function(suppressEvent){
23967         if(this.snapshot && this.snapshot != this.data){
23968             this.data = this.snapshot;
23969             delete this.snapshot;
23970             if(suppressEvent !== true){
23971                 this.fireEvent("datachanged", this);
23972             }
23973         }
23974     },
23975
23976     // private
23977     afterEdit : function(record){
23978         if(this.modified.indexOf(record) == -1){
23979             this.modified.push(record);
23980         }
23981         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23982     },
23983     
23984     // private
23985     afterReject : function(record){
23986         this.modified.remove(record);
23987         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23988     },
23989
23990     // private
23991     afterCommit : function(record){
23992         this.modified.remove(record);
23993         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23994     },
23995
23996     /**
23997      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23998      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23999      */
24000     commitChanges : function(){
24001         var m = this.modified.slice(0);
24002         this.modified = [];
24003         for(var i = 0, len = m.length; i < len; i++){
24004             m[i].commit();
24005         }
24006     },
24007
24008     /**
24009      * Cancel outstanding changes on all changed records.
24010      */
24011     rejectChanges : function(){
24012         var m = this.modified.slice(0);
24013         this.modified = [];
24014         for(var i = 0, len = m.length; i < len; i++){
24015             m[i].reject();
24016         }
24017     },
24018
24019     onMetaChange : function(meta, rtype, o){
24020         this.recordType = rtype;
24021         this.fields = rtype.prototype.fields;
24022         delete this.snapshot;
24023         this.sortInfo = meta.sortInfo || this.sortInfo;
24024         this.modified = [];
24025         this.fireEvent('metachange', this, this.reader.meta);
24026     },
24027     
24028     moveIndex : function(data, type)
24029     {
24030         var index = this.indexOf(data);
24031         
24032         var newIndex = index + type;
24033         
24034         this.remove(data);
24035         
24036         this.insert(newIndex, data);
24037         
24038     }
24039 });/*
24040  * Based on:
24041  * Ext JS Library 1.1.1
24042  * Copyright(c) 2006-2007, Ext JS, LLC.
24043  *
24044  * Originally Released Under LGPL - original licence link has changed is not relivant.
24045  *
24046  * Fork - LGPL
24047  * <script type="text/javascript">
24048  */
24049
24050 /**
24051  * @class Roo.data.SimpleStore
24052  * @extends Roo.data.Store
24053  * Small helper class to make creating Stores from Array data easier.
24054  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24055  * @cfg {Array} fields An array of field definition objects, or field name strings.
24056  * @cfg {Object} an existing reader (eg. copied from another store)
24057  * @cfg {Array} data The multi-dimensional array of data
24058  * @constructor
24059  * @param {Object} config
24060  */
24061 Roo.data.SimpleStore = function(config)
24062 {
24063     Roo.data.SimpleStore.superclass.constructor.call(this, {
24064         isLocal : true,
24065         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24066                 id: config.id
24067             },
24068             Roo.data.Record.create(config.fields)
24069         ),
24070         proxy : new Roo.data.MemoryProxy(config.data)
24071     });
24072     this.load();
24073 };
24074 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24075  * Based on:
24076  * Ext JS Library 1.1.1
24077  * Copyright(c) 2006-2007, Ext JS, LLC.
24078  *
24079  * Originally Released Under LGPL - original licence link has changed is not relivant.
24080  *
24081  * Fork - LGPL
24082  * <script type="text/javascript">
24083  */
24084
24085 /**
24086 /**
24087  * @extends Roo.data.Store
24088  * @class Roo.data.JsonStore
24089  * Small helper class to make creating Stores for JSON data easier. <br/>
24090 <pre><code>
24091 var store = new Roo.data.JsonStore({
24092     url: 'get-images.php',
24093     root: 'images',
24094     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24095 });
24096 </code></pre>
24097  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24098  * JsonReader and HttpProxy (unless inline data is provided).</b>
24099  * @cfg {Array} fields An array of field definition objects, or field name strings.
24100  * @constructor
24101  * @param {Object} config
24102  */
24103 Roo.data.JsonStore = function(c){
24104     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24105         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24106         reader: new Roo.data.JsonReader(c, c.fields)
24107     }));
24108 };
24109 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24110  * Based on:
24111  * Ext JS Library 1.1.1
24112  * Copyright(c) 2006-2007, Ext JS, LLC.
24113  *
24114  * Originally Released Under LGPL - original licence link has changed is not relivant.
24115  *
24116  * Fork - LGPL
24117  * <script type="text/javascript">
24118  */
24119
24120  
24121 Roo.data.Field = function(config){
24122     if(typeof config == "string"){
24123         config = {name: config};
24124     }
24125     Roo.apply(this, config);
24126     
24127     if(!this.type){
24128         this.type = "auto";
24129     }
24130     
24131     var st = Roo.data.SortTypes;
24132     // named sortTypes are supported, here we look them up
24133     if(typeof this.sortType == "string"){
24134         this.sortType = st[this.sortType];
24135     }
24136     
24137     // set default sortType for strings and dates
24138     if(!this.sortType){
24139         switch(this.type){
24140             case "string":
24141                 this.sortType = st.asUCString;
24142                 break;
24143             case "date":
24144                 this.sortType = st.asDate;
24145                 break;
24146             default:
24147                 this.sortType = st.none;
24148         }
24149     }
24150
24151     // define once
24152     var stripRe = /[\$,%]/g;
24153
24154     // prebuilt conversion function for this field, instead of
24155     // switching every time we're reading a value
24156     if(!this.convert){
24157         var cv, dateFormat = this.dateFormat;
24158         switch(this.type){
24159             case "":
24160             case "auto":
24161             case undefined:
24162                 cv = function(v){ return v; };
24163                 break;
24164             case "string":
24165                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24166                 break;
24167             case "int":
24168                 cv = function(v){
24169                     return v !== undefined && v !== null && v !== '' ?
24170                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24171                     };
24172                 break;
24173             case "float":
24174                 cv = function(v){
24175                     return v !== undefined && v !== null && v !== '' ?
24176                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24177                     };
24178                 break;
24179             case "bool":
24180             case "boolean":
24181                 cv = function(v){ return v === true || v === "true" || v == 1; };
24182                 break;
24183             case "date":
24184                 cv = function(v){
24185                     if(!v){
24186                         return '';
24187                     }
24188                     if(v instanceof Date){
24189                         return v;
24190                     }
24191                     if(dateFormat){
24192                         if(dateFormat == "timestamp"){
24193                             return new Date(v*1000);
24194                         }
24195                         return Date.parseDate(v, dateFormat);
24196                     }
24197                     var parsed = Date.parse(v);
24198                     return parsed ? new Date(parsed) : null;
24199                 };
24200              break;
24201             
24202         }
24203         this.convert = cv;
24204     }
24205 };
24206
24207 Roo.data.Field.prototype = {
24208     dateFormat: null,
24209     defaultValue: "",
24210     mapping: null,
24211     sortType : null,
24212     sortDir : "ASC"
24213 };/*
24214  * Based on:
24215  * Ext JS Library 1.1.1
24216  * Copyright(c) 2006-2007, Ext JS, LLC.
24217  *
24218  * Originally Released Under LGPL - original licence link has changed is not relivant.
24219  *
24220  * Fork - LGPL
24221  * <script type="text/javascript">
24222  */
24223  
24224 // Base class for reading structured data from a data source.  This class is intended to be
24225 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24226
24227 /**
24228  * @class Roo.data.DataReader
24229  * Base class for reading structured data from a data source.  This class is intended to be
24230  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24231  */
24232
24233 Roo.data.DataReader = function(meta, recordType){
24234     
24235     this.meta = meta;
24236     
24237     this.recordType = recordType instanceof Array ? 
24238         Roo.data.Record.create(recordType) : recordType;
24239 };
24240
24241 Roo.data.DataReader.prototype = {
24242     
24243     
24244     readerType : 'Data',
24245      /**
24246      * Create an empty record
24247      * @param {Object} data (optional) - overlay some values
24248      * @return {Roo.data.Record} record created.
24249      */
24250     newRow :  function(d) {
24251         var da =  {};
24252         this.recordType.prototype.fields.each(function(c) {
24253             switch( c.type) {
24254                 case 'int' : da[c.name] = 0; break;
24255                 case 'date' : da[c.name] = new Date(); break;
24256                 case 'float' : da[c.name] = 0.0; break;
24257                 case 'boolean' : da[c.name] = false; break;
24258                 default : da[c.name] = ""; break;
24259             }
24260             
24261         });
24262         return new this.recordType(Roo.apply(da, d));
24263     }
24264     
24265     
24266 };/*
24267  * Based on:
24268  * Ext JS Library 1.1.1
24269  * Copyright(c) 2006-2007, Ext JS, LLC.
24270  *
24271  * Originally Released Under LGPL - original licence link has changed is not relivant.
24272  *
24273  * Fork - LGPL
24274  * <script type="text/javascript">
24275  */
24276
24277 /**
24278  * @class Roo.data.DataProxy
24279  * @extends Roo.data.Observable
24280  * This class is an abstract base class for implementations which provide retrieval of
24281  * unformatted data objects.<br>
24282  * <p>
24283  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24284  * (of the appropriate type which knows how to parse the data object) to provide a block of
24285  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24286  * <p>
24287  * Custom implementations must implement the load method as described in
24288  * {@link Roo.data.HttpProxy#load}.
24289  */
24290 Roo.data.DataProxy = function(){
24291     this.addEvents({
24292         /**
24293          * @event beforeload
24294          * Fires before a network request is made to retrieve a data object.
24295          * @param {Object} This DataProxy object.
24296          * @param {Object} params The params parameter to the load function.
24297          */
24298         beforeload : true,
24299         /**
24300          * @event load
24301          * Fires before the load method's callback is called.
24302          * @param {Object} This DataProxy object.
24303          * @param {Object} o The data object.
24304          * @param {Object} arg The callback argument object passed to the load function.
24305          */
24306         load : true,
24307         /**
24308          * @event loadexception
24309          * Fires if an Exception occurs during data retrieval.
24310          * @param {Object} This DataProxy object.
24311          * @param {Object} o The data object.
24312          * @param {Object} arg The callback argument object passed to the load function.
24313          * @param {Object} e The Exception.
24314          */
24315         loadexception : true
24316     });
24317     Roo.data.DataProxy.superclass.constructor.call(this);
24318 };
24319
24320 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24321
24322     /**
24323      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24324      */
24325 /*
24326  * Based on:
24327  * Ext JS Library 1.1.1
24328  * Copyright(c) 2006-2007, Ext JS, LLC.
24329  *
24330  * Originally Released Under LGPL - original licence link has changed is not relivant.
24331  *
24332  * Fork - LGPL
24333  * <script type="text/javascript">
24334  */
24335 /**
24336  * @class Roo.data.MemoryProxy
24337  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24338  * to the Reader when its load method is called.
24339  * @constructor
24340  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24341  */
24342 Roo.data.MemoryProxy = function(data){
24343     if (data.data) {
24344         data = data.data;
24345     }
24346     Roo.data.MemoryProxy.superclass.constructor.call(this);
24347     this.data = data;
24348 };
24349
24350 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24351     
24352     /**
24353      * Load data from the requested source (in this case an in-memory
24354      * data object passed to the constructor), read the data object into
24355      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24356      * process that block using the passed callback.
24357      * @param {Object} params This parameter is not used by the MemoryProxy class.
24358      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24359      * object into a block of Roo.data.Records.
24360      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24361      * The function must be passed <ul>
24362      * <li>The Record block object</li>
24363      * <li>The "arg" argument from the load function</li>
24364      * <li>A boolean success indicator</li>
24365      * </ul>
24366      * @param {Object} scope The scope in which to call the callback
24367      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24368      */
24369     load : function(params, reader, callback, scope, arg){
24370         params = params || {};
24371         var result;
24372         try {
24373             result = reader.readRecords(params.data ? params.data :this.data);
24374         }catch(e){
24375             this.fireEvent("loadexception", this, arg, null, e);
24376             callback.call(scope, null, arg, false);
24377             return;
24378         }
24379         callback.call(scope, result, arg, true);
24380     },
24381     
24382     // private
24383     update : function(params, records){
24384         
24385     }
24386 });/*
24387  * Based on:
24388  * Ext JS Library 1.1.1
24389  * Copyright(c) 2006-2007, Ext JS, LLC.
24390  *
24391  * Originally Released Under LGPL - original licence link has changed is not relivant.
24392  *
24393  * Fork - LGPL
24394  * <script type="text/javascript">
24395  */
24396 /**
24397  * @class Roo.data.HttpProxy
24398  * @extends Roo.data.DataProxy
24399  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24400  * configured to reference a certain URL.<br><br>
24401  * <p>
24402  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24403  * from which the running page was served.<br><br>
24404  * <p>
24405  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24406  * <p>
24407  * Be aware that to enable the browser to parse an XML document, the server must set
24408  * the Content-Type header in the HTTP response to "text/xml".
24409  * @constructor
24410  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24411  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24412  * will be used to make the request.
24413  */
24414 Roo.data.HttpProxy = function(conn){
24415     Roo.data.HttpProxy.superclass.constructor.call(this);
24416     // is conn a conn config or a real conn?
24417     this.conn = conn;
24418     this.useAjax = !conn || !conn.events;
24419   
24420 };
24421
24422 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24423     // thse are take from connection...
24424     
24425     /**
24426      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24427      */
24428     /**
24429      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24430      * extra parameters to each request made by this object. (defaults to undefined)
24431      */
24432     /**
24433      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24434      *  to each request made by this object. (defaults to undefined)
24435      */
24436     /**
24437      * @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)
24438      */
24439     /**
24440      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24441      */
24442      /**
24443      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24444      * @type Boolean
24445      */
24446   
24447
24448     /**
24449      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24450      * @type Boolean
24451      */
24452     /**
24453      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24454      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24455      * a finer-grained basis than the DataProxy events.
24456      */
24457     getConnection : function(){
24458         return this.useAjax ? Roo.Ajax : this.conn;
24459     },
24460
24461     /**
24462      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24463      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24464      * process that block using the passed callback.
24465      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24466      * for the request to the remote server.
24467      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24468      * object into a block of Roo.data.Records.
24469      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24470      * The function must be passed <ul>
24471      * <li>The Record block object</li>
24472      * <li>The "arg" argument from the load function</li>
24473      * <li>A boolean success indicator</li>
24474      * </ul>
24475      * @param {Object} scope The scope in which to call the callback
24476      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24477      */
24478     load : function(params, reader, callback, scope, arg){
24479         if(this.fireEvent("beforeload", this, params) !== false){
24480             var  o = {
24481                 params : params || {},
24482                 request: {
24483                     callback : callback,
24484                     scope : scope,
24485                     arg : arg
24486                 },
24487                 reader: reader,
24488                 callback : this.loadResponse,
24489                 scope: this
24490             };
24491             if(this.useAjax){
24492                 Roo.applyIf(o, this.conn);
24493                 if(this.activeRequest){
24494                     Roo.Ajax.abort(this.activeRequest);
24495                 }
24496                 this.activeRequest = Roo.Ajax.request(o);
24497             }else{
24498                 this.conn.request(o);
24499             }
24500         }else{
24501             callback.call(scope||this, null, arg, false);
24502         }
24503     },
24504
24505     // private
24506     loadResponse : function(o, success, response){
24507         delete this.activeRequest;
24508         if(!success){
24509             this.fireEvent("loadexception", this, o, response);
24510             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24511             return;
24512         }
24513         var result;
24514         try {
24515             result = o.reader.read(response);
24516         }catch(e){
24517             this.fireEvent("loadexception", this, o, response, e);
24518             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24519             return;
24520         }
24521         
24522         this.fireEvent("load", this, o, o.request.arg);
24523         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24524     },
24525
24526     // private
24527     update : function(dataSet){
24528
24529     },
24530
24531     // private
24532     updateResponse : function(dataSet){
24533
24534     }
24535 });/*
24536  * Based on:
24537  * Ext JS Library 1.1.1
24538  * Copyright(c) 2006-2007, Ext JS, LLC.
24539  *
24540  * Originally Released Under LGPL - original licence link has changed is not relivant.
24541  *
24542  * Fork - LGPL
24543  * <script type="text/javascript">
24544  */
24545
24546 /**
24547  * @class Roo.data.ScriptTagProxy
24548  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24549  * other than the originating domain of the running page.<br><br>
24550  * <p>
24551  * <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
24552  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24553  * <p>
24554  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24555  * source code that is used as the source inside a &lt;script> tag.<br><br>
24556  * <p>
24557  * In order for the browser to process the returned data, the server must wrap the data object
24558  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24559  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24560  * depending on whether the callback name was passed:
24561  * <p>
24562  * <pre><code>
24563 boolean scriptTag = false;
24564 String cb = request.getParameter("callback");
24565 if (cb != null) {
24566     scriptTag = true;
24567     response.setContentType("text/javascript");
24568 } else {
24569     response.setContentType("application/x-json");
24570 }
24571 Writer out = response.getWriter();
24572 if (scriptTag) {
24573     out.write(cb + "(");
24574 }
24575 out.print(dataBlock.toJsonString());
24576 if (scriptTag) {
24577     out.write(");");
24578 }
24579 </pre></code>
24580  *
24581  * @constructor
24582  * @param {Object} config A configuration object.
24583  */
24584 Roo.data.ScriptTagProxy = function(config){
24585     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24586     Roo.apply(this, config);
24587     this.head = document.getElementsByTagName("head")[0];
24588 };
24589
24590 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24591
24592 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24593     /**
24594      * @cfg {String} url The URL from which to request the data object.
24595      */
24596     /**
24597      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24598      */
24599     timeout : 30000,
24600     /**
24601      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24602      * the server the name of the callback function set up by the load call to process the returned data object.
24603      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24604      * javascript output which calls this named function passing the data object as its only parameter.
24605      */
24606     callbackParam : "callback",
24607     /**
24608      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24609      * name to the request.
24610      */
24611     nocache : true,
24612
24613     /**
24614      * Load data from the configured URL, read the data object into
24615      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24616      * process that block using the passed callback.
24617      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24618      * for the request to the remote server.
24619      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24620      * object into a block of Roo.data.Records.
24621      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24622      * The function must be passed <ul>
24623      * <li>The Record block object</li>
24624      * <li>The "arg" argument from the load function</li>
24625      * <li>A boolean success indicator</li>
24626      * </ul>
24627      * @param {Object} scope The scope in which to call the callback
24628      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24629      */
24630     load : function(params, reader, callback, scope, arg){
24631         if(this.fireEvent("beforeload", this, params) !== false){
24632
24633             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24634
24635             var url = this.url;
24636             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24637             if(this.nocache){
24638                 url += "&_dc=" + (new Date().getTime());
24639             }
24640             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24641             var trans = {
24642                 id : transId,
24643                 cb : "stcCallback"+transId,
24644                 scriptId : "stcScript"+transId,
24645                 params : params,
24646                 arg : arg,
24647                 url : url,
24648                 callback : callback,
24649                 scope : scope,
24650                 reader : reader
24651             };
24652             var conn = this;
24653
24654             window[trans.cb] = function(o){
24655                 conn.handleResponse(o, trans);
24656             };
24657
24658             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24659
24660             if(this.autoAbort !== false){
24661                 this.abort();
24662             }
24663
24664             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24665
24666             var script = document.createElement("script");
24667             script.setAttribute("src", url);
24668             script.setAttribute("type", "text/javascript");
24669             script.setAttribute("id", trans.scriptId);
24670             this.head.appendChild(script);
24671
24672             this.trans = trans;
24673         }else{
24674             callback.call(scope||this, null, arg, false);
24675         }
24676     },
24677
24678     // private
24679     isLoading : function(){
24680         return this.trans ? true : false;
24681     },
24682
24683     /**
24684      * Abort the current server request.
24685      */
24686     abort : function(){
24687         if(this.isLoading()){
24688             this.destroyTrans(this.trans);
24689         }
24690     },
24691
24692     // private
24693     destroyTrans : function(trans, isLoaded){
24694         this.head.removeChild(document.getElementById(trans.scriptId));
24695         clearTimeout(trans.timeoutId);
24696         if(isLoaded){
24697             window[trans.cb] = undefined;
24698             try{
24699                 delete window[trans.cb];
24700             }catch(e){}
24701         }else{
24702             // if hasn't been loaded, wait for load to remove it to prevent script error
24703             window[trans.cb] = function(){
24704                 window[trans.cb] = undefined;
24705                 try{
24706                     delete window[trans.cb];
24707                 }catch(e){}
24708             };
24709         }
24710     },
24711
24712     // private
24713     handleResponse : function(o, trans){
24714         this.trans = false;
24715         this.destroyTrans(trans, true);
24716         var result;
24717         try {
24718             result = trans.reader.readRecords(o);
24719         }catch(e){
24720             this.fireEvent("loadexception", this, o, trans.arg, e);
24721             trans.callback.call(trans.scope||window, null, trans.arg, false);
24722             return;
24723         }
24724         this.fireEvent("load", this, o, trans.arg);
24725         trans.callback.call(trans.scope||window, result, trans.arg, true);
24726     },
24727
24728     // private
24729     handleFailure : function(trans){
24730         this.trans = false;
24731         this.destroyTrans(trans, false);
24732         this.fireEvent("loadexception", this, null, trans.arg);
24733         trans.callback.call(trans.scope||window, null, trans.arg, false);
24734     }
24735 });/*
24736  * Based on:
24737  * Ext JS Library 1.1.1
24738  * Copyright(c) 2006-2007, Ext JS, LLC.
24739  *
24740  * Originally Released Under LGPL - original licence link has changed is not relivant.
24741  *
24742  * Fork - LGPL
24743  * <script type="text/javascript">
24744  */
24745
24746 /**
24747  * @class Roo.data.JsonReader
24748  * @extends Roo.data.DataReader
24749  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24750  * based on mappings in a provided Roo.data.Record constructor.
24751  * 
24752  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24753  * in the reply previously. 
24754  * 
24755  * <p>
24756  * Example code:
24757  * <pre><code>
24758 var RecordDef = Roo.data.Record.create([
24759     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24760     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24761 ]);
24762 var myReader = new Roo.data.JsonReader({
24763     totalProperty: "results",    // The property which contains the total dataset size (optional)
24764     root: "rows",                // The property which contains an Array of row objects
24765     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24766 }, RecordDef);
24767 </code></pre>
24768  * <p>
24769  * This would consume a JSON file like this:
24770  * <pre><code>
24771 { 'results': 2, 'rows': [
24772     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24773     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24774 }
24775 </code></pre>
24776  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24777  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24778  * paged from the remote server.
24779  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24780  * @cfg {String} root name of the property which contains the Array of row objects.
24781  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24782  * @cfg {Array} fields Array of field definition objects
24783  * @constructor
24784  * Create a new JsonReader
24785  * @param {Object} meta Metadata configuration options
24786  * @param {Object} recordType Either an Array of field definition objects,
24787  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24788  */
24789 Roo.data.JsonReader = function(meta, recordType){
24790     
24791     meta = meta || {};
24792     // set some defaults:
24793     Roo.applyIf(meta, {
24794         totalProperty: 'total',
24795         successProperty : 'success',
24796         root : 'data',
24797         id : 'id'
24798     });
24799     
24800     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24801 };
24802 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24803     
24804     readerType : 'Json',
24805     
24806     /**
24807      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24808      * Used by Store query builder to append _requestMeta to params.
24809      * 
24810      */
24811     metaFromRemote : false,
24812     /**
24813      * This method is only used by a DataProxy which has retrieved data from a remote server.
24814      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24815      * @return {Object} data A data block which is used by an Roo.data.Store object as
24816      * a cache of Roo.data.Records.
24817      */
24818     read : function(response){
24819         var json = response.responseText;
24820        
24821         var o = /* eval:var:o */ eval("("+json+")");
24822         if(!o) {
24823             throw {message: "JsonReader.read: Json object not found"};
24824         }
24825         
24826         if(o.metaData){
24827             
24828             delete this.ef;
24829             this.metaFromRemote = true;
24830             this.meta = o.metaData;
24831             this.recordType = Roo.data.Record.create(o.metaData.fields);
24832             this.onMetaChange(this.meta, this.recordType, o);
24833         }
24834         return this.readRecords(o);
24835     },
24836
24837     // private function a store will implement
24838     onMetaChange : function(meta, recordType, o){
24839
24840     },
24841
24842     /**
24843          * @ignore
24844          */
24845     simpleAccess: function(obj, subsc) {
24846         return obj[subsc];
24847     },
24848
24849         /**
24850          * @ignore
24851          */
24852     getJsonAccessor: function(){
24853         var re = /[\[\.]/;
24854         return function(expr) {
24855             try {
24856                 return(re.test(expr))
24857                     ? new Function("obj", "return obj." + expr)
24858                     : function(obj){
24859                         return obj[expr];
24860                     };
24861             } catch(e){}
24862             return Roo.emptyFn;
24863         };
24864     }(),
24865
24866     /**
24867      * Create a data block containing Roo.data.Records from an XML document.
24868      * @param {Object} o An object which contains an Array of row objects in the property specified
24869      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24870      * which contains the total size of the dataset.
24871      * @return {Object} data A data block which is used by an Roo.data.Store object as
24872      * a cache of Roo.data.Records.
24873      */
24874     readRecords : function(o){
24875         /**
24876          * After any data loads, the raw JSON data is available for further custom processing.
24877          * @type Object
24878          */
24879         this.o = o;
24880         var s = this.meta, Record = this.recordType,
24881             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24882
24883 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24884         if (!this.ef) {
24885             if(s.totalProperty) {
24886                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24887                 }
24888                 if(s.successProperty) {
24889                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24890                 }
24891                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24892                 if (s.id) {
24893                         var g = this.getJsonAccessor(s.id);
24894                         this.getId = function(rec) {
24895                                 var r = g(rec);  
24896                                 return (r === undefined || r === "") ? null : r;
24897                         };
24898                 } else {
24899                         this.getId = function(){return null;};
24900                 }
24901             this.ef = [];
24902             for(var jj = 0; jj < fl; jj++){
24903                 f = fi[jj];
24904                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24905                 this.ef[jj] = this.getJsonAccessor(map);
24906             }
24907         }
24908
24909         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24910         if(s.totalProperty){
24911             var vt = parseInt(this.getTotal(o), 10);
24912             if(!isNaN(vt)){
24913                 totalRecords = vt;
24914             }
24915         }
24916         if(s.successProperty){
24917             var vs = this.getSuccess(o);
24918             if(vs === false || vs === 'false'){
24919                 success = false;
24920             }
24921         }
24922         var records = [];
24923         for(var i = 0; i < c; i++){
24924                 var n = root[i];
24925             var values = {};
24926             var id = this.getId(n);
24927             for(var j = 0; j < fl; j++){
24928                 f = fi[j];
24929             var v = this.ef[j](n);
24930             if (!f.convert) {
24931                 Roo.log('missing convert for ' + f.name);
24932                 Roo.log(f);
24933                 continue;
24934             }
24935             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24936             }
24937             var record = new Record(values, id);
24938             record.json = n;
24939             records[i] = record;
24940         }
24941         return {
24942             raw : o,
24943             success : success,
24944             records : records,
24945             totalRecords : totalRecords
24946         };
24947     },
24948     // used when loading children.. @see loadDataFromChildren
24949     toLoadData: function(rec)
24950     {
24951         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24952         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24953         return { data : data, total : data.length };
24954         
24955     }
24956 });/*
24957  * Based on:
24958  * Ext JS Library 1.1.1
24959  * Copyright(c) 2006-2007, Ext JS, LLC.
24960  *
24961  * Originally Released Under LGPL - original licence link has changed is not relivant.
24962  *
24963  * Fork - LGPL
24964  * <script type="text/javascript">
24965  */
24966
24967 /**
24968  * @class Roo.data.XmlReader
24969  * @extends Roo.data.DataReader
24970  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24971  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24972  * <p>
24973  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24974  * header in the HTTP response must be set to "text/xml".</em>
24975  * <p>
24976  * Example code:
24977  * <pre><code>
24978 var RecordDef = Roo.data.Record.create([
24979    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24980    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24981 ]);
24982 var myReader = new Roo.data.XmlReader({
24983    totalRecords: "results", // The element which contains the total dataset size (optional)
24984    record: "row",           // The repeated element which contains row information
24985    id: "id"                 // The element within the row that provides an ID for the record (optional)
24986 }, RecordDef);
24987 </code></pre>
24988  * <p>
24989  * This would consume an XML file like this:
24990  * <pre><code>
24991 &lt;?xml?>
24992 &lt;dataset>
24993  &lt;results>2&lt;/results>
24994  &lt;row>
24995    &lt;id>1&lt;/id>
24996    &lt;name>Bill&lt;/name>
24997    &lt;occupation>Gardener&lt;/occupation>
24998  &lt;/row>
24999  &lt;row>
25000    &lt;id>2&lt;/id>
25001    &lt;name>Ben&lt;/name>
25002    &lt;occupation>Horticulturalist&lt;/occupation>
25003  &lt;/row>
25004 &lt;/dataset>
25005 </code></pre>
25006  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25007  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25008  * paged from the remote server.
25009  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25010  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25011  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25012  * a record identifier value.
25013  * @constructor
25014  * Create a new XmlReader
25015  * @param {Object} meta Metadata configuration options
25016  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25017  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25018  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25019  */
25020 Roo.data.XmlReader = function(meta, recordType){
25021     meta = meta || {};
25022     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25023 };
25024 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25025     
25026     readerType : 'Xml',
25027     
25028     /**
25029      * This method is only used by a DataProxy which has retrieved data from a remote server.
25030          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25031          * to contain a method called 'responseXML' that returns an XML document object.
25032      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25033      * a cache of Roo.data.Records.
25034      */
25035     read : function(response){
25036         var doc = response.responseXML;
25037         if(!doc) {
25038             throw {message: "XmlReader.read: XML Document not available"};
25039         }
25040         return this.readRecords(doc);
25041     },
25042
25043     /**
25044      * Create a data block containing Roo.data.Records from an XML document.
25045          * @param {Object} doc A parsed XML document.
25046      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25047      * a cache of Roo.data.Records.
25048      */
25049     readRecords : function(doc){
25050         /**
25051          * After any data loads/reads, the raw XML Document is available for further custom processing.
25052          * @type XMLDocument
25053          */
25054         this.xmlData = doc;
25055         var root = doc.documentElement || doc;
25056         var q = Roo.DomQuery;
25057         var recordType = this.recordType, fields = recordType.prototype.fields;
25058         var sid = this.meta.id;
25059         var totalRecords = 0, success = true;
25060         if(this.meta.totalRecords){
25061             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25062         }
25063         
25064         if(this.meta.success){
25065             var sv = q.selectValue(this.meta.success, root, true);
25066             success = sv !== false && sv !== 'false';
25067         }
25068         var records = [];
25069         var ns = q.select(this.meta.record, root);
25070         for(var i = 0, len = ns.length; i < len; i++) {
25071                 var n = ns[i];
25072                 var values = {};
25073                 var id = sid ? q.selectValue(sid, n) : undefined;
25074                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25075                     var f = fields.items[j];
25076                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25077                     v = f.convert(v);
25078                     values[f.name] = v;
25079                 }
25080                 var record = new recordType(values, id);
25081                 record.node = n;
25082                 records[records.length] = record;
25083             }
25084
25085             return {
25086                 success : success,
25087                 records : records,
25088                 totalRecords : totalRecords || records.length
25089             };
25090     }
25091 });/*
25092  * Based on:
25093  * Ext JS Library 1.1.1
25094  * Copyright(c) 2006-2007, Ext JS, LLC.
25095  *
25096  * Originally Released Under LGPL - original licence link has changed is not relivant.
25097  *
25098  * Fork - LGPL
25099  * <script type="text/javascript">
25100  */
25101
25102 /**
25103  * @class Roo.data.ArrayReader
25104  * @extends Roo.data.DataReader
25105  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25106  * Each element of that Array represents a row of data fields. The
25107  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25108  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25109  * <p>
25110  * Example code:.
25111  * <pre><code>
25112 var RecordDef = Roo.data.Record.create([
25113     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25114     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25115 ]);
25116 var myReader = new Roo.data.ArrayReader({
25117     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25118 }, RecordDef);
25119 </code></pre>
25120  * <p>
25121  * This would consume an Array like this:
25122  * <pre><code>
25123 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25124   </code></pre>
25125  
25126  * @constructor
25127  * Create a new JsonReader
25128  * @param {Object} meta Metadata configuration options.
25129  * @param {Object|Array} recordType Either an Array of field definition objects
25130  * 
25131  * @cfg {Array} fields Array of field definition objects
25132  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25133  * as specified to {@link Roo.data.Record#create},
25134  * or an {@link Roo.data.Record} object
25135  *
25136  * 
25137  * created using {@link Roo.data.Record#create}.
25138  */
25139 Roo.data.ArrayReader = function(meta, recordType)
25140 {    
25141     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25142 };
25143
25144 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25145     
25146       /**
25147      * Create a data block containing Roo.data.Records from an XML document.
25148      * @param {Object} o An Array of row objects which represents the dataset.
25149      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25150      * a cache of Roo.data.Records.
25151      */
25152     readRecords : function(o)
25153     {
25154         var sid = this.meta ? this.meta.id : null;
25155         var recordType = this.recordType, fields = recordType.prototype.fields;
25156         var records = [];
25157         var root = o;
25158         for(var i = 0; i < root.length; i++){
25159                 var n = root[i];
25160             var values = {};
25161             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25162             for(var j = 0, jlen = fields.length; j < jlen; j++){
25163                 var f = fields.items[j];
25164                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25165                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25166                 v = f.convert(v);
25167                 values[f.name] = v;
25168             }
25169             var record = new recordType(values, id);
25170             record.json = n;
25171             records[records.length] = record;
25172         }
25173         return {
25174             records : records,
25175             totalRecords : records.length
25176         };
25177     },
25178     // used when loading children.. @see loadDataFromChildren
25179     toLoadData: function(rec)
25180     {
25181         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25182         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25183         
25184     }
25185     
25186     
25187 });/*
25188  * Based on:
25189  * Ext JS Library 1.1.1
25190  * Copyright(c) 2006-2007, Ext JS, LLC.
25191  *
25192  * Originally Released Under LGPL - original licence link has changed is not relivant.
25193  *
25194  * Fork - LGPL
25195  * <script type="text/javascript">
25196  */
25197
25198
25199 /**
25200  * @class Roo.data.Tree
25201  * @extends Roo.util.Observable
25202  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25203  * in the tree have most standard DOM functionality.
25204  * @constructor
25205  * @param {Node} root (optional) The root node
25206  */
25207 Roo.data.Tree = function(root){
25208    this.nodeHash = {};
25209    /**
25210     * The root node for this tree
25211     * @type Node
25212     */
25213    this.root = null;
25214    if(root){
25215        this.setRootNode(root);
25216    }
25217    this.addEvents({
25218        /**
25219         * @event append
25220         * Fires when a new child node is appended to a node in this tree.
25221         * @param {Tree} tree The owner tree
25222         * @param {Node} parent The parent node
25223         * @param {Node} node The newly appended node
25224         * @param {Number} index The index of the newly appended node
25225         */
25226        "append" : true,
25227        /**
25228         * @event remove
25229         * Fires when a child node is removed from a node in this tree.
25230         * @param {Tree} tree The owner tree
25231         * @param {Node} parent The parent node
25232         * @param {Node} node The child node removed
25233         */
25234        "remove" : true,
25235        /**
25236         * @event move
25237         * Fires when a node is moved to a new location in the tree
25238         * @param {Tree} tree The owner tree
25239         * @param {Node} node The node moved
25240         * @param {Node} oldParent The old parent of this node
25241         * @param {Node} newParent The new parent of this node
25242         * @param {Number} index The index it was moved to
25243         */
25244        "move" : true,
25245        /**
25246         * @event insert
25247         * Fires when a new child node is inserted in a node in this tree.
25248         * @param {Tree} tree The owner tree
25249         * @param {Node} parent The parent node
25250         * @param {Node} node The child node inserted
25251         * @param {Node} refNode The child node the node was inserted before
25252         */
25253        "insert" : true,
25254        /**
25255         * @event beforeappend
25256         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25257         * @param {Tree} tree The owner tree
25258         * @param {Node} parent The parent node
25259         * @param {Node} node The child node to be appended
25260         */
25261        "beforeappend" : true,
25262        /**
25263         * @event beforeremove
25264         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25265         * @param {Tree} tree The owner tree
25266         * @param {Node} parent The parent node
25267         * @param {Node} node The child node to be removed
25268         */
25269        "beforeremove" : true,
25270        /**
25271         * @event beforemove
25272         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25273         * @param {Tree} tree The owner tree
25274         * @param {Node} node The node being moved
25275         * @param {Node} oldParent The parent of the node
25276         * @param {Node} newParent The new parent the node is moving to
25277         * @param {Number} index The index it is being moved to
25278         */
25279        "beforemove" : true,
25280        /**
25281         * @event beforeinsert
25282         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25283         * @param {Tree} tree The owner tree
25284         * @param {Node} parent The parent node
25285         * @param {Node} node The child node to be inserted
25286         * @param {Node} refNode The child node the node is being inserted before
25287         */
25288        "beforeinsert" : true
25289    });
25290
25291     Roo.data.Tree.superclass.constructor.call(this);
25292 };
25293
25294 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25295     pathSeparator: "/",
25296
25297     proxyNodeEvent : function(){
25298         return this.fireEvent.apply(this, arguments);
25299     },
25300
25301     /**
25302      * Returns the root node for this tree.
25303      * @return {Node}
25304      */
25305     getRootNode : function(){
25306         return this.root;
25307     },
25308
25309     /**
25310      * Sets the root node for this tree.
25311      * @param {Node} node
25312      * @return {Node}
25313      */
25314     setRootNode : function(node){
25315         this.root = node;
25316         node.ownerTree = this;
25317         node.isRoot = true;
25318         this.registerNode(node);
25319         return node;
25320     },
25321
25322     /**
25323      * Gets a node in this tree by its id.
25324      * @param {String} id
25325      * @return {Node}
25326      */
25327     getNodeById : function(id){
25328         return this.nodeHash[id];
25329     },
25330
25331     registerNode : function(node){
25332         this.nodeHash[node.id] = node;
25333     },
25334
25335     unregisterNode : function(node){
25336         delete this.nodeHash[node.id];
25337     },
25338
25339     toString : function(){
25340         return "[Tree"+(this.id?" "+this.id:"")+"]";
25341     }
25342 });
25343
25344 /**
25345  * @class Roo.data.Node
25346  * @extends Roo.util.Observable
25347  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25348  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25349  * @constructor
25350  * @param {Object} attributes The attributes/config for the node
25351  */
25352 Roo.data.Node = function(attributes){
25353     /**
25354      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25355      * @type {Object}
25356      */
25357     this.attributes = attributes || {};
25358     this.leaf = this.attributes.leaf;
25359     /**
25360      * The node id. @type String
25361      */
25362     this.id = this.attributes.id;
25363     if(!this.id){
25364         this.id = Roo.id(null, "ynode-");
25365         this.attributes.id = this.id;
25366     }
25367      
25368     
25369     /**
25370      * All child nodes of this node. @type Array
25371      */
25372     this.childNodes = [];
25373     if(!this.childNodes.indexOf){ // indexOf is a must
25374         this.childNodes.indexOf = function(o){
25375             for(var i = 0, len = this.length; i < len; i++){
25376                 if(this[i] == o) {
25377                     return i;
25378                 }
25379             }
25380             return -1;
25381         };
25382     }
25383     /**
25384      * The parent node for this node. @type Node
25385      */
25386     this.parentNode = null;
25387     /**
25388      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25389      */
25390     this.firstChild = null;
25391     /**
25392      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25393      */
25394     this.lastChild = null;
25395     /**
25396      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25397      */
25398     this.previousSibling = null;
25399     /**
25400      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25401      */
25402     this.nextSibling = null;
25403
25404     this.addEvents({
25405        /**
25406         * @event append
25407         * Fires when a new child node is appended
25408         * @param {Tree} tree The owner tree
25409         * @param {Node} this This node
25410         * @param {Node} node The newly appended node
25411         * @param {Number} index The index of the newly appended node
25412         */
25413        "append" : true,
25414        /**
25415         * @event remove
25416         * Fires when a child node is removed
25417         * @param {Tree} tree The owner tree
25418         * @param {Node} this This node
25419         * @param {Node} node The removed node
25420         */
25421        "remove" : true,
25422        /**
25423         * @event move
25424         * Fires when this node is moved to a new location in the tree
25425         * @param {Tree} tree The owner tree
25426         * @param {Node} this This node
25427         * @param {Node} oldParent The old parent of this node
25428         * @param {Node} newParent The new parent of this node
25429         * @param {Number} index The index it was moved to
25430         */
25431        "move" : true,
25432        /**
25433         * @event insert
25434         * Fires when a new child node is inserted.
25435         * @param {Tree} tree The owner tree
25436         * @param {Node} this This node
25437         * @param {Node} node The child node inserted
25438         * @param {Node} refNode The child node the node was inserted before
25439         */
25440        "insert" : true,
25441        /**
25442         * @event beforeappend
25443         * Fires before a new child is appended, return false to cancel the append.
25444         * @param {Tree} tree The owner tree
25445         * @param {Node} this This node
25446         * @param {Node} node The child node to be appended
25447         */
25448        "beforeappend" : true,
25449        /**
25450         * @event beforeremove
25451         * Fires before a child is removed, return false to cancel the remove.
25452         * @param {Tree} tree The owner tree
25453         * @param {Node} this This node
25454         * @param {Node} node The child node to be removed
25455         */
25456        "beforeremove" : true,
25457        /**
25458         * @event beforemove
25459         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25460         * @param {Tree} tree The owner tree
25461         * @param {Node} this This node
25462         * @param {Node} oldParent The parent of this node
25463         * @param {Node} newParent The new parent this node is moving to
25464         * @param {Number} index The index it is being moved to
25465         */
25466        "beforemove" : true,
25467        /**
25468         * @event beforeinsert
25469         * Fires before a new child is inserted, return false to cancel the insert.
25470         * @param {Tree} tree The owner tree
25471         * @param {Node} this This node
25472         * @param {Node} node The child node to be inserted
25473         * @param {Node} refNode The child node the node is being inserted before
25474         */
25475        "beforeinsert" : true
25476    });
25477     this.listeners = this.attributes.listeners;
25478     Roo.data.Node.superclass.constructor.call(this);
25479 };
25480
25481 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25482     fireEvent : function(evtName){
25483         // first do standard event for this node
25484         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25485             return false;
25486         }
25487         // then bubble it up to the tree if the event wasn't cancelled
25488         var ot = this.getOwnerTree();
25489         if(ot){
25490             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25491                 return false;
25492             }
25493         }
25494         return true;
25495     },
25496
25497     /**
25498      * Returns true if this node is a leaf
25499      * @return {Boolean}
25500      */
25501     isLeaf : function(){
25502         return this.leaf === true;
25503     },
25504
25505     // private
25506     setFirstChild : function(node){
25507         this.firstChild = node;
25508     },
25509
25510     //private
25511     setLastChild : function(node){
25512         this.lastChild = node;
25513     },
25514
25515
25516     /**
25517      * Returns true if this node is the last child of its parent
25518      * @return {Boolean}
25519      */
25520     isLast : function(){
25521        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25522     },
25523
25524     /**
25525      * Returns true if this node is the first child of its parent
25526      * @return {Boolean}
25527      */
25528     isFirst : function(){
25529        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25530     },
25531
25532     hasChildNodes : function(){
25533         return !this.isLeaf() && this.childNodes.length > 0;
25534     },
25535
25536     /**
25537      * Insert node(s) as the last child node of this node.
25538      * @param {Node/Array} node The node or Array of nodes to append
25539      * @return {Node} The appended node if single append, or null if an array was passed
25540      */
25541     appendChild : function(node){
25542         var multi = false;
25543         if(node instanceof Array){
25544             multi = node;
25545         }else if(arguments.length > 1){
25546             multi = arguments;
25547         }
25548         
25549         // if passed an array or multiple args do them one by one
25550         if(multi){
25551             for(var i = 0, len = multi.length; i < len; i++) {
25552                 this.appendChild(multi[i]);
25553             }
25554         }else{
25555             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25556                 return false;
25557             }
25558             var index = this.childNodes.length;
25559             var oldParent = node.parentNode;
25560             // it's a move, make sure we move it cleanly
25561             if(oldParent){
25562                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25563                     return false;
25564                 }
25565                 oldParent.removeChild(node);
25566             }
25567             
25568             index = this.childNodes.length;
25569             if(index == 0){
25570                 this.setFirstChild(node);
25571             }
25572             this.childNodes.push(node);
25573             node.parentNode = this;
25574             var ps = this.childNodes[index-1];
25575             if(ps){
25576                 node.previousSibling = ps;
25577                 ps.nextSibling = node;
25578             }else{
25579                 node.previousSibling = null;
25580             }
25581             node.nextSibling = null;
25582             this.setLastChild(node);
25583             node.setOwnerTree(this.getOwnerTree());
25584             this.fireEvent("append", this.ownerTree, this, node, index);
25585             if(this.ownerTree) {
25586                 this.ownerTree.fireEvent("appendnode", this, node, index);
25587             }
25588             if(oldParent){
25589                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25590             }
25591             return node;
25592         }
25593     },
25594
25595     /**
25596      * Removes a child node from this node.
25597      * @param {Node} node The node to remove
25598      * @return {Node} The removed node
25599      */
25600     removeChild : function(node){
25601         var index = this.childNodes.indexOf(node);
25602         if(index == -1){
25603             return false;
25604         }
25605         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25606             return false;
25607         }
25608
25609         // remove it from childNodes collection
25610         this.childNodes.splice(index, 1);
25611
25612         // update siblings
25613         if(node.previousSibling){
25614             node.previousSibling.nextSibling = node.nextSibling;
25615         }
25616         if(node.nextSibling){
25617             node.nextSibling.previousSibling = node.previousSibling;
25618         }
25619
25620         // update child refs
25621         if(this.firstChild == node){
25622             this.setFirstChild(node.nextSibling);
25623         }
25624         if(this.lastChild == node){
25625             this.setLastChild(node.previousSibling);
25626         }
25627
25628         node.setOwnerTree(null);
25629         // clear any references from the node
25630         node.parentNode = null;
25631         node.previousSibling = null;
25632         node.nextSibling = null;
25633         this.fireEvent("remove", this.ownerTree, this, node);
25634         return node;
25635     },
25636
25637     /**
25638      * Inserts the first node before the second node in this nodes childNodes collection.
25639      * @param {Node} node The node to insert
25640      * @param {Node} refNode The node to insert before (if null the node is appended)
25641      * @return {Node} The inserted node
25642      */
25643     insertBefore : function(node, refNode){
25644         if(!refNode){ // like standard Dom, refNode can be null for append
25645             return this.appendChild(node);
25646         }
25647         // nothing to do
25648         if(node == refNode){
25649             return false;
25650         }
25651
25652         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25653             return false;
25654         }
25655         var index = this.childNodes.indexOf(refNode);
25656         var oldParent = node.parentNode;
25657         var refIndex = index;
25658
25659         // when moving internally, indexes will change after remove
25660         if(oldParent == this && this.childNodes.indexOf(node) < index){
25661             refIndex--;
25662         }
25663
25664         // it's a move, make sure we move it cleanly
25665         if(oldParent){
25666             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25667                 return false;
25668             }
25669             oldParent.removeChild(node);
25670         }
25671         if(refIndex == 0){
25672             this.setFirstChild(node);
25673         }
25674         this.childNodes.splice(refIndex, 0, node);
25675         node.parentNode = this;
25676         var ps = this.childNodes[refIndex-1];
25677         if(ps){
25678             node.previousSibling = ps;
25679             ps.nextSibling = node;
25680         }else{
25681             node.previousSibling = null;
25682         }
25683         node.nextSibling = refNode;
25684         refNode.previousSibling = node;
25685         node.setOwnerTree(this.getOwnerTree());
25686         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25687         if(oldParent){
25688             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25689         }
25690         return node;
25691     },
25692
25693     /**
25694      * Returns the child node at the specified index.
25695      * @param {Number} index
25696      * @return {Node}
25697      */
25698     item : function(index){
25699         return this.childNodes[index];
25700     },
25701
25702     /**
25703      * Replaces one child node in this node with another.
25704      * @param {Node} newChild The replacement node
25705      * @param {Node} oldChild The node to replace
25706      * @return {Node} The replaced node
25707      */
25708     replaceChild : function(newChild, oldChild){
25709         this.insertBefore(newChild, oldChild);
25710         this.removeChild(oldChild);
25711         return oldChild;
25712     },
25713
25714     /**
25715      * Returns the index of a child node
25716      * @param {Node} node
25717      * @return {Number} The index of the node or -1 if it was not found
25718      */
25719     indexOf : function(child){
25720         return this.childNodes.indexOf(child);
25721     },
25722
25723     /**
25724      * Returns the tree this node is in.
25725      * @return {Tree}
25726      */
25727     getOwnerTree : function(){
25728         // if it doesn't have one, look for one
25729         if(!this.ownerTree){
25730             var p = this;
25731             while(p){
25732                 if(p.ownerTree){
25733                     this.ownerTree = p.ownerTree;
25734                     break;
25735                 }
25736                 p = p.parentNode;
25737             }
25738         }
25739         return this.ownerTree;
25740     },
25741
25742     /**
25743      * Returns depth of this node (the root node has a depth of 0)
25744      * @return {Number}
25745      */
25746     getDepth : function(){
25747         var depth = 0;
25748         var p = this;
25749         while(p.parentNode){
25750             ++depth;
25751             p = p.parentNode;
25752         }
25753         return depth;
25754     },
25755
25756     // private
25757     setOwnerTree : function(tree){
25758         // if it's move, we need to update everyone
25759         if(tree != this.ownerTree){
25760             if(this.ownerTree){
25761                 this.ownerTree.unregisterNode(this);
25762             }
25763             this.ownerTree = tree;
25764             var cs = this.childNodes;
25765             for(var i = 0, len = cs.length; i < len; i++) {
25766                 cs[i].setOwnerTree(tree);
25767             }
25768             if(tree){
25769                 tree.registerNode(this);
25770             }
25771         }
25772     },
25773
25774     /**
25775      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25776      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25777      * @return {String} The path
25778      */
25779     getPath : function(attr){
25780         attr = attr || "id";
25781         var p = this.parentNode;
25782         var b = [this.attributes[attr]];
25783         while(p){
25784             b.unshift(p.attributes[attr]);
25785             p = p.parentNode;
25786         }
25787         var sep = this.getOwnerTree().pathSeparator;
25788         return sep + b.join(sep);
25789     },
25790
25791     /**
25792      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25793      * function call will be the scope provided or the current node. The arguments to the function
25794      * will be the args provided or the current node. If the function returns false at any point,
25795      * the bubble is stopped.
25796      * @param {Function} fn The function to call
25797      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25798      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25799      */
25800     bubble : function(fn, scope, args){
25801         var p = this;
25802         while(p){
25803             if(fn.call(scope || p, args || p) === false){
25804                 break;
25805             }
25806             p = p.parentNode;
25807         }
25808     },
25809
25810     /**
25811      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25812      * function call will be the scope provided or the current node. The arguments to the function
25813      * will be the args provided or the current node. If the function returns false at any point,
25814      * the cascade is stopped on that branch.
25815      * @param {Function} fn The function to call
25816      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25817      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25818      */
25819     cascade : function(fn, scope, args){
25820         if(fn.call(scope || this, args || this) !== false){
25821             var cs = this.childNodes;
25822             for(var i = 0, len = cs.length; i < len; i++) {
25823                 cs[i].cascade(fn, scope, args);
25824             }
25825         }
25826     },
25827
25828     /**
25829      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25830      * function call will be the scope provided or the current node. The arguments to the function
25831      * will be the args provided or the current node. If the function returns false at any point,
25832      * the iteration stops.
25833      * @param {Function} fn The function to call
25834      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25835      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25836      */
25837     eachChild : function(fn, scope, args){
25838         var cs = this.childNodes;
25839         for(var i = 0, len = cs.length; i < len; i++) {
25840                 if(fn.call(scope || this, args || cs[i]) === false){
25841                     break;
25842                 }
25843         }
25844     },
25845
25846     /**
25847      * Finds the first child that has the attribute with the specified value.
25848      * @param {String} attribute The attribute name
25849      * @param {Mixed} value The value to search for
25850      * @return {Node} The found child or null if none was found
25851      */
25852     findChild : function(attribute, value){
25853         var cs = this.childNodes;
25854         for(var i = 0, len = cs.length; i < len; i++) {
25855                 if(cs[i].attributes[attribute] == value){
25856                     return cs[i];
25857                 }
25858         }
25859         return null;
25860     },
25861
25862     /**
25863      * Finds the first child by a custom function. The child matches if the function passed
25864      * returns true.
25865      * @param {Function} fn
25866      * @param {Object} scope (optional)
25867      * @return {Node} The found child or null if none was found
25868      */
25869     findChildBy : function(fn, scope){
25870         var cs = this.childNodes;
25871         for(var i = 0, len = cs.length; i < len; i++) {
25872                 if(fn.call(scope||cs[i], cs[i]) === true){
25873                     return cs[i];
25874                 }
25875         }
25876         return null;
25877     },
25878
25879     /**
25880      * Sorts this nodes children using the supplied sort function
25881      * @param {Function} fn
25882      * @param {Object} scope (optional)
25883      */
25884     sort : function(fn, scope){
25885         var cs = this.childNodes;
25886         var len = cs.length;
25887         if(len > 0){
25888             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25889             cs.sort(sortFn);
25890             for(var i = 0; i < len; i++){
25891                 var n = cs[i];
25892                 n.previousSibling = cs[i-1];
25893                 n.nextSibling = cs[i+1];
25894                 if(i == 0){
25895                     this.setFirstChild(n);
25896                 }
25897                 if(i == len-1){
25898                     this.setLastChild(n);
25899                 }
25900             }
25901         }
25902     },
25903
25904     /**
25905      * Returns true if this node is an ancestor (at any point) of the passed node.
25906      * @param {Node} node
25907      * @return {Boolean}
25908      */
25909     contains : function(node){
25910         return node.isAncestor(this);
25911     },
25912
25913     /**
25914      * Returns true if the passed node is an ancestor (at any point) of this node.
25915      * @param {Node} node
25916      * @return {Boolean}
25917      */
25918     isAncestor : function(node){
25919         var p = this.parentNode;
25920         while(p){
25921             if(p == node){
25922                 return true;
25923             }
25924             p = p.parentNode;
25925         }
25926         return false;
25927     },
25928
25929     toString : function(){
25930         return "[Node"+(this.id?" "+this.id:"")+"]";
25931     }
25932 });/*
25933  * Based on:
25934  * Ext JS Library 1.1.1
25935  * Copyright(c) 2006-2007, Ext JS, LLC.
25936  *
25937  * Originally Released Under LGPL - original licence link has changed is not relivant.
25938  *
25939  * Fork - LGPL
25940  * <script type="text/javascript">
25941  */
25942
25943
25944 /**
25945  * @class Roo.Shadow
25946  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25947  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25948  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25949  * @constructor
25950  * Create a new Shadow
25951  * @param {Object} config The config object
25952  */
25953 Roo.Shadow = function(config){
25954     Roo.apply(this, config);
25955     if(typeof this.mode != "string"){
25956         this.mode = this.defaultMode;
25957     }
25958     var o = this.offset, a = {h: 0};
25959     var rad = Math.floor(this.offset/2);
25960     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25961         case "drop":
25962             a.w = 0;
25963             a.l = a.t = o;
25964             a.t -= 1;
25965             if(Roo.isIE){
25966                 a.l -= this.offset + rad;
25967                 a.t -= this.offset + rad;
25968                 a.w -= rad;
25969                 a.h -= rad;
25970                 a.t += 1;
25971             }
25972         break;
25973         case "sides":
25974             a.w = (o*2);
25975             a.l = -o;
25976             a.t = o-1;
25977             if(Roo.isIE){
25978                 a.l -= (this.offset - rad);
25979                 a.t -= this.offset + rad;
25980                 a.l += 1;
25981                 a.w -= (this.offset - rad)*2;
25982                 a.w -= rad + 1;
25983                 a.h -= 1;
25984             }
25985         break;
25986         case "frame":
25987             a.w = a.h = (o*2);
25988             a.l = a.t = -o;
25989             a.t += 1;
25990             a.h -= 2;
25991             if(Roo.isIE){
25992                 a.l -= (this.offset - rad);
25993                 a.t -= (this.offset - rad);
25994                 a.l += 1;
25995                 a.w -= (this.offset + rad + 1);
25996                 a.h -= (this.offset + rad);
25997                 a.h += 1;
25998             }
25999         break;
26000     };
26001
26002     this.adjusts = a;
26003 };
26004
26005 Roo.Shadow.prototype = {
26006     /**
26007      * @cfg {String} mode
26008      * The shadow display mode.  Supports the following options:<br />
26009      * sides: Shadow displays on both sides and bottom only<br />
26010      * frame: Shadow displays equally on all four sides<br />
26011      * drop: Traditional bottom-right drop shadow (default)
26012      */
26013     /**
26014      * @cfg {String} offset
26015      * The number of pixels to offset the shadow from the element (defaults to 4)
26016      */
26017     offset: 4,
26018
26019     // private
26020     defaultMode: "drop",
26021
26022     /**
26023      * Displays the shadow under the target element
26024      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26025      */
26026     show : function(target){
26027         target = Roo.get(target);
26028         if(!this.el){
26029             this.el = Roo.Shadow.Pool.pull();
26030             if(this.el.dom.nextSibling != target.dom){
26031                 this.el.insertBefore(target);
26032             }
26033         }
26034         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26035         if(Roo.isIE){
26036             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26037         }
26038         this.realign(
26039             target.getLeft(true),
26040             target.getTop(true),
26041             target.getWidth(),
26042             target.getHeight()
26043         );
26044         this.el.dom.style.display = "block";
26045     },
26046
26047     /**
26048      * Returns true if the shadow is visible, else false
26049      */
26050     isVisible : function(){
26051         return this.el ? true : false;  
26052     },
26053
26054     /**
26055      * Direct alignment when values are already available. Show must be called at least once before
26056      * calling this method to ensure it is initialized.
26057      * @param {Number} left The target element left position
26058      * @param {Number} top The target element top position
26059      * @param {Number} width The target element width
26060      * @param {Number} height The target element height
26061      */
26062     realign : function(l, t, w, h){
26063         if(!this.el){
26064             return;
26065         }
26066         var a = this.adjusts, d = this.el.dom, s = d.style;
26067         var iea = 0;
26068         s.left = (l+a.l)+"px";
26069         s.top = (t+a.t)+"px";
26070         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26071  
26072         if(s.width != sws || s.height != shs){
26073             s.width = sws;
26074             s.height = shs;
26075             if(!Roo.isIE){
26076                 var cn = d.childNodes;
26077                 var sww = Math.max(0, (sw-12))+"px";
26078                 cn[0].childNodes[1].style.width = sww;
26079                 cn[1].childNodes[1].style.width = sww;
26080                 cn[2].childNodes[1].style.width = sww;
26081                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26082             }
26083         }
26084     },
26085
26086     /**
26087      * Hides this shadow
26088      */
26089     hide : function(){
26090         if(this.el){
26091             this.el.dom.style.display = "none";
26092             Roo.Shadow.Pool.push(this.el);
26093             delete this.el;
26094         }
26095     },
26096
26097     /**
26098      * Adjust the z-index of this shadow
26099      * @param {Number} zindex The new z-index
26100      */
26101     setZIndex : function(z){
26102         this.zIndex = z;
26103         if(this.el){
26104             this.el.setStyle("z-index", z);
26105         }
26106     }
26107 };
26108
26109 // Private utility class that manages the internal Shadow cache
26110 Roo.Shadow.Pool = function(){
26111     var p = [];
26112     var markup = Roo.isIE ?
26113                  '<div class="x-ie-shadow"></div>' :
26114                  '<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>';
26115     return {
26116         pull : function(){
26117             var sh = p.shift();
26118             if(!sh){
26119                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26120                 sh.autoBoxAdjust = false;
26121             }
26122             return sh;
26123         },
26124
26125         push : function(sh){
26126             p.push(sh);
26127         }
26128     };
26129 }();/*
26130  * Based on:
26131  * Ext JS Library 1.1.1
26132  * Copyright(c) 2006-2007, Ext JS, LLC.
26133  *
26134  * Originally Released Under LGPL - original licence link has changed is not relivant.
26135  *
26136  * Fork - LGPL
26137  * <script type="text/javascript">
26138  */
26139
26140
26141 /**
26142  * @class Roo.SplitBar
26143  * @extends Roo.util.Observable
26144  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26145  * <br><br>
26146  * Usage:
26147  * <pre><code>
26148 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26149                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26150 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26151 split.minSize = 100;
26152 split.maxSize = 600;
26153 split.animate = true;
26154 split.on('moved', splitterMoved);
26155 </code></pre>
26156  * @constructor
26157  * Create a new SplitBar
26158  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26159  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26160  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26161  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26162                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26163                         position of the SplitBar).
26164  */
26165 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26166     
26167     /** @private */
26168     this.el = Roo.get(dragElement, true);
26169     this.el.dom.unselectable = "on";
26170     /** @private */
26171     this.resizingEl = Roo.get(resizingElement, true);
26172
26173     /**
26174      * @private
26175      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26176      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26177      * @type Number
26178      */
26179     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26180     
26181     /**
26182      * The minimum size of the resizing element. (Defaults to 0)
26183      * @type Number
26184      */
26185     this.minSize = 0;
26186     
26187     /**
26188      * The maximum size of the resizing element. (Defaults to 2000)
26189      * @type Number
26190      */
26191     this.maxSize = 2000;
26192     
26193     /**
26194      * Whether to animate the transition to the new size
26195      * @type Boolean
26196      */
26197     this.animate = false;
26198     
26199     /**
26200      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26201      * @type Boolean
26202      */
26203     this.useShim = false;
26204     
26205     /** @private */
26206     this.shim = null;
26207     
26208     if(!existingProxy){
26209         /** @private */
26210         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26211     }else{
26212         this.proxy = Roo.get(existingProxy).dom;
26213     }
26214     /** @private */
26215     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26216     
26217     /** @private */
26218     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26219     
26220     /** @private */
26221     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26222     
26223     /** @private */
26224     this.dragSpecs = {};
26225     
26226     /**
26227      * @private The adapter to use to positon and resize elements
26228      */
26229     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26230     this.adapter.init(this);
26231     
26232     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26233         /** @private */
26234         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26235         this.el.addClass("x-splitbar-h");
26236     }else{
26237         /** @private */
26238         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26239         this.el.addClass("x-splitbar-v");
26240     }
26241     
26242     this.addEvents({
26243         /**
26244          * @event resize
26245          * Fires when the splitter is moved (alias for {@link #event-moved})
26246          * @param {Roo.SplitBar} this
26247          * @param {Number} newSize the new width or height
26248          */
26249         "resize" : true,
26250         /**
26251          * @event moved
26252          * Fires when the splitter is moved
26253          * @param {Roo.SplitBar} this
26254          * @param {Number} newSize the new width or height
26255          */
26256         "moved" : true,
26257         /**
26258          * @event beforeresize
26259          * Fires before the splitter is dragged
26260          * @param {Roo.SplitBar} this
26261          */
26262         "beforeresize" : true,
26263
26264         "beforeapply" : true
26265     });
26266
26267     Roo.util.Observable.call(this);
26268 };
26269
26270 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26271     onStartProxyDrag : function(x, y){
26272         this.fireEvent("beforeresize", this);
26273         if(!this.overlay){
26274             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26275             o.unselectable();
26276             o.enableDisplayMode("block");
26277             // all splitbars share the same overlay
26278             Roo.SplitBar.prototype.overlay = o;
26279         }
26280         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26281         this.overlay.show();
26282         Roo.get(this.proxy).setDisplayed("block");
26283         var size = this.adapter.getElementSize(this);
26284         this.activeMinSize = this.getMinimumSize();;
26285         this.activeMaxSize = this.getMaximumSize();;
26286         var c1 = size - this.activeMinSize;
26287         var c2 = Math.max(this.activeMaxSize - size, 0);
26288         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26289             this.dd.resetConstraints();
26290             this.dd.setXConstraint(
26291                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26292                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26293             );
26294             this.dd.setYConstraint(0, 0);
26295         }else{
26296             this.dd.resetConstraints();
26297             this.dd.setXConstraint(0, 0);
26298             this.dd.setYConstraint(
26299                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26300                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26301             );
26302          }
26303         this.dragSpecs.startSize = size;
26304         this.dragSpecs.startPoint = [x, y];
26305         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26306     },
26307     
26308     /** 
26309      * @private Called after the drag operation by the DDProxy
26310      */
26311     onEndProxyDrag : function(e){
26312         Roo.get(this.proxy).setDisplayed(false);
26313         var endPoint = Roo.lib.Event.getXY(e);
26314         if(this.overlay){
26315             this.overlay.hide();
26316         }
26317         var newSize;
26318         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26319             newSize = this.dragSpecs.startSize + 
26320                 (this.placement == Roo.SplitBar.LEFT ?
26321                     endPoint[0] - this.dragSpecs.startPoint[0] :
26322                     this.dragSpecs.startPoint[0] - endPoint[0]
26323                 );
26324         }else{
26325             newSize = this.dragSpecs.startSize + 
26326                 (this.placement == Roo.SplitBar.TOP ?
26327                     endPoint[1] - this.dragSpecs.startPoint[1] :
26328                     this.dragSpecs.startPoint[1] - endPoint[1]
26329                 );
26330         }
26331         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26332         if(newSize != this.dragSpecs.startSize){
26333             if(this.fireEvent('beforeapply', this, newSize) !== false){
26334                 this.adapter.setElementSize(this, newSize);
26335                 this.fireEvent("moved", this, newSize);
26336                 this.fireEvent("resize", this, newSize);
26337             }
26338         }
26339     },
26340     
26341     /**
26342      * Get the adapter this SplitBar uses
26343      * @return The adapter object
26344      */
26345     getAdapter : function(){
26346         return this.adapter;
26347     },
26348     
26349     /**
26350      * Set the adapter this SplitBar uses
26351      * @param {Object} adapter A SplitBar adapter object
26352      */
26353     setAdapter : function(adapter){
26354         this.adapter = adapter;
26355         this.adapter.init(this);
26356     },
26357     
26358     /**
26359      * Gets the minimum size for the resizing element
26360      * @return {Number} The minimum size
26361      */
26362     getMinimumSize : function(){
26363         return this.minSize;
26364     },
26365     
26366     /**
26367      * Sets the minimum size for the resizing element
26368      * @param {Number} minSize The minimum size
26369      */
26370     setMinimumSize : function(minSize){
26371         this.minSize = minSize;
26372     },
26373     
26374     /**
26375      * Gets the maximum size for the resizing element
26376      * @return {Number} The maximum size
26377      */
26378     getMaximumSize : function(){
26379         return this.maxSize;
26380     },
26381     
26382     /**
26383      * Sets the maximum size for the resizing element
26384      * @param {Number} maxSize The maximum size
26385      */
26386     setMaximumSize : function(maxSize){
26387         this.maxSize = maxSize;
26388     },
26389     
26390     /**
26391      * Sets the initialize size for the resizing element
26392      * @param {Number} size The initial size
26393      */
26394     setCurrentSize : function(size){
26395         var oldAnimate = this.animate;
26396         this.animate = false;
26397         this.adapter.setElementSize(this, size);
26398         this.animate = oldAnimate;
26399     },
26400     
26401     /**
26402      * Destroy this splitbar. 
26403      * @param {Boolean} removeEl True to remove the element
26404      */
26405     destroy : function(removeEl){
26406         if(this.shim){
26407             this.shim.remove();
26408         }
26409         this.dd.unreg();
26410         this.proxy.parentNode.removeChild(this.proxy);
26411         if(removeEl){
26412             this.el.remove();
26413         }
26414     }
26415 });
26416
26417 /**
26418  * @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.
26419  */
26420 Roo.SplitBar.createProxy = function(dir){
26421     var proxy = new Roo.Element(document.createElement("div"));
26422     proxy.unselectable();
26423     var cls = 'x-splitbar-proxy';
26424     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26425     document.body.appendChild(proxy.dom);
26426     return proxy.dom;
26427 };
26428
26429 /** 
26430  * @class Roo.SplitBar.BasicLayoutAdapter
26431  * Default Adapter. It assumes the splitter and resizing element are not positioned
26432  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26433  */
26434 Roo.SplitBar.BasicLayoutAdapter = function(){
26435 };
26436
26437 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26438     // do nothing for now
26439     init : function(s){
26440     
26441     },
26442     /**
26443      * Called before drag operations to get the current size of the resizing element. 
26444      * @param {Roo.SplitBar} s The SplitBar using this adapter
26445      */
26446      getElementSize : function(s){
26447         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26448             return s.resizingEl.getWidth();
26449         }else{
26450             return s.resizingEl.getHeight();
26451         }
26452     },
26453     
26454     /**
26455      * Called after drag operations to set the size of the resizing element.
26456      * @param {Roo.SplitBar} s The SplitBar using this adapter
26457      * @param {Number} newSize The new size to set
26458      * @param {Function} onComplete A function to be invoked when resizing is complete
26459      */
26460     setElementSize : function(s, newSize, onComplete){
26461         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26462             if(!s.animate){
26463                 s.resizingEl.setWidth(newSize);
26464                 if(onComplete){
26465                     onComplete(s, newSize);
26466                 }
26467             }else{
26468                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26469             }
26470         }else{
26471             
26472             if(!s.animate){
26473                 s.resizingEl.setHeight(newSize);
26474                 if(onComplete){
26475                     onComplete(s, newSize);
26476                 }
26477             }else{
26478                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26479             }
26480         }
26481     }
26482 };
26483
26484 /** 
26485  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26486  * @extends Roo.SplitBar.BasicLayoutAdapter
26487  * Adapter that  moves the splitter element to align with the resized sizing element. 
26488  * Used with an absolute positioned SplitBar.
26489  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26490  * document.body, make sure you assign an id to the body element.
26491  */
26492 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26493     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26494     this.container = Roo.get(container);
26495 };
26496
26497 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26498     init : function(s){
26499         this.basic.init(s);
26500     },
26501     
26502     getElementSize : function(s){
26503         return this.basic.getElementSize(s);
26504     },
26505     
26506     setElementSize : function(s, newSize, onComplete){
26507         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26508     },
26509     
26510     moveSplitter : function(s){
26511         var yes = Roo.SplitBar;
26512         switch(s.placement){
26513             case yes.LEFT:
26514                 s.el.setX(s.resizingEl.getRight());
26515                 break;
26516             case yes.RIGHT:
26517                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26518                 break;
26519             case yes.TOP:
26520                 s.el.setY(s.resizingEl.getBottom());
26521                 break;
26522             case yes.BOTTOM:
26523                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26524                 break;
26525         }
26526     }
26527 };
26528
26529 /**
26530  * Orientation constant - Create a vertical SplitBar
26531  * @static
26532  * @type Number
26533  */
26534 Roo.SplitBar.VERTICAL = 1;
26535
26536 /**
26537  * Orientation constant - Create a horizontal SplitBar
26538  * @static
26539  * @type Number
26540  */
26541 Roo.SplitBar.HORIZONTAL = 2;
26542
26543 /**
26544  * Placement constant - The resizing element is to the left of the splitter element
26545  * @static
26546  * @type Number
26547  */
26548 Roo.SplitBar.LEFT = 1;
26549
26550 /**
26551  * Placement constant - The resizing element is to the right of the splitter element
26552  * @static
26553  * @type Number
26554  */
26555 Roo.SplitBar.RIGHT = 2;
26556
26557 /**
26558  * Placement constant - The resizing element is positioned above the splitter element
26559  * @static
26560  * @type Number
26561  */
26562 Roo.SplitBar.TOP = 3;
26563
26564 /**
26565  * Placement constant - The resizing element is positioned under splitter element
26566  * @static
26567  * @type Number
26568  */
26569 Roo.SplitBar.BOTTOM = 4;
26570 /*
26571  * Based on:
26572  * Ext JS Library 1.1.1
26573  * Copyright(c) 2006-2007, Ext JS, LLC.
26574  *
26575  * Originally Released Under LGPL - original licence link has changed is not relivant.
26576  *
26577  * Fork - LGPL
26578  * <script type="text/javascript">
26579  */
26580
26581 /**
26582  * @class Roo.View
26583  * @extends Roo.util.Observable
26584  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26585  * This class also supports single and multi selection modes. <br>
26586  * Create a data model bound view:
26587  <pre><code>
26588  var store = new Roo.data.Store(...);
26589
26590  var view = new Roo.View({
26591     el : "my-element",
26592     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26593  
26594     singleSelect: true,
26595     selectedClass: "ydataview-selected",
26596     store: store
26597  });
26598
26599  // listen for node click?
26600  view.on("click", function(vw, index, node, e){
26601  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26602  });
26603
26604  // load XML data
26605  dataModel.load("foobar.xml");
26606  </code></pre>
26607  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26608  * <br><br>
26609  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26610  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26611  * 
26612  * Note: old style constructor is still suported (container, template, config)
26613  * 
26614  * @constructor
26615  * Create a new View
26616  * @param {Object} config The config object
26617  * 
26618  */
26619 Roo.View = function(config, depreciated_tpl, depreciated_config){
26620     
26621     this.parent = false;
26622     
26623     if (typeof(depreciated_tpl) == 'undefined') {
26624         // new way.. - universal constructor.
26625         Roo.apply(this, config);
26626         this.el  = Roo.get(this.el);
26627     } else {
26628         // old format..
26629         this.el  = Roo.get(config);
26630         this.tpl = depreciated_tpl;
26631         Roo.apply(this, depreciated_config);
26632     }
26633     this.wrapEl  = this.el.wrap().wrap();
26634     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26635     
26636     
26637     if(typeof(this.tpl) == "string"){
26638         this.tpl = new Roo.Template(this.tpl);
26639     } else {
26640         // support xtype ctors..
26641         this.tpl = new Roo.factory(this.tpl, Roo);
26642     }
26643     
26644     
26645     this.tpl.compile();
26646     
26647     /** @private */
26648     this.addEvents({
26649         /**
26650          * @event beforeclick
26651          * Fires before a click is processed. Returns false to cancel the default action.
26652          * @param {Roo.View} this
26653          * @param {Number} index The index of the target node
26654          * @param {HTMLElement} node The target node
26655          * @param {Roo.EventObject} e The raw event object
26656          */
26657             "beforeclick" : true,
26658         /**
26659          * @event click
26660          * Fires when a template node is clicked.
26661          * @param {Roo.View} this
26662          * @param {Number} index The index of the target node
26663          * @param {HTMLElement} node The target node
26664          * @param {Roo.EventObject} e The raw event object
26665          */
26666             "click" : true,
26667         /**
26668          * @event dblclick
26669          * Fires when a template node is double clicked.
26670          * @param {Roo.View} this
26671          * @param {Number} index The index of the target node
26672          * @param {HTMLElement} node The target node
26673          * @param {Roo.EventObject} e The raw event object
26674          */
26675             "dblclick" : true,
26676         /**
26677          * @event contextmenu
26678          * Fires when a template node is right clicked.
26679          * @param {Roo.View} this
26680          * @param {Number} index The index of the target node
26681          * @param {HTMLElement} node The target node
26682          * @param {Roo.EventObject} e The raw event object
26683          */
26684             "contextmenu" : true,
26685         /**
26686          * @event selectionchange
26687          * Fires when the selected nodes change.
26688          * @param {Roo.View} this
26689          * @param {Array} selections Array of the selected nodes
26690          */
26691             "selectionchange" : true,
26692     
26693         /**
26694          * @event beforeselect
26695          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26696          * @param {Roo.View} this
26697          * @param {HTMLElement} node The node to be selected
26698          * @param {Array} selections Array of currently selected nodes
26699          */
26700             "beforeselect" : true,
26701         /**
26702          * @event preparedata
26703          * Fires on every row to render, to allow you to change the data.
26704          * @param {Roo.View} this
26705          * @param {Object} data to be rendered (change this)
26706          */
26707           "preparedata" : true
26708           
26709           
26710         });
26711
26712
26713
26714     this.el.on({
26715         "click": this.onClick,
26716         "dblclick": this.onDblClick,
26717         "contextmenu": this.onContextMenu,
26718         scope:this
26719     });
26720
26721     this.selections = [];
26722     this.nodes = [];
26723     this.cmp = new Roo.CompositeElementLite([]);
26724     if(this.store){
26725         this.store = Roo.factory(this.store, Roo.data);
26726         this.setStore(this.store, true);
26727     }
26728     
26729     if ( this.footer && this.footer.xtype) {
26730            
26731          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26732         
26733         this.footer.dataSource = this.store;
26734         this.footer.container = fctr;
26735         this.footer = Roo.factory(this.footer, Roo);
26736         fctr.insertFirst(this.el);
26737         
26738         // this is a bit insane - as the paging toolbar seems to detach the el..
26739 //        dom.parentNode.parentNode.parentNode
26740          // they get detached?
26741     }
26742     
26743     
26744     Roo.View.superclass.constructor.call(this);
26745     
26746     
26747 };
26748
26749 Roo.extend(Roo.View, Roo.util.Observable, {
26750     
26751      /**
26752      * @cfg {Roo.data.Store} store Data store to load data from.
26753      */
26754     store : false,
26755     
26756     /**
26757      * @cfg {String|Roo.Element} el The container element.
26758      */
26759     el : '',
26760     
26761     /**
26762      * @cfg {String|Roo.Template} tpl The template used by this View 
26763      */
26764     tpl : false,
26765     /**
26766      * @cfg {String} dataName the named area of the template to use as the data area
26767      *                          Works with domtemplates roo-name="name"
26768      */
26769     dataName: false,
26770     /**
26771      * @cfg {String} selectedClass The css class to add to selected nodes
26772      */
26773     selectedClass : "x-view-selected",
26774      /**
26775      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26776      */
26777     emptyText : "",
26778     
26779     /**
26780      * @cfg {String} text to display on mask (default Loading)
26781      */
26782     mask : false,
26783     /**
26784      * @cfg {Boolean} multiSelect Allow multiple selection
26785      */
26786     multiSelect : false,
26787     /**
26788      * @cfg {Boolean} singleSelect Allow single selection
26789      */
26790     singleSelect:  false,
26791     
26792     /**
26793      * @cfg {Boolean} toggleSelect - selecting 
26794      */
26795     toggleSelect : false,
26796     
26797     /**
26798      * @cfg {Boolean} tickable - selecting 
26799      */
26800     tickable : false,
26801     
26802     /**
26803      * Returns the element this view is bound to.
26804      * @return {Roo.Element}
26805      */
26806     getEl : function(){
26807         return this.wrapEl;
26808     },
26809     
26810     
26811
26812     /**
26813      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26814      */
26815     refresh : function(){
26816         //Roo.log('refresh');
26817         var t = this.tpl;
26818         
26819         // if we are using something like 'domtemplate', then
26820         // the what gets used is:
26821         // t.applySubtemplate(NAME, data, wrapping data..)
26822         // the outer template then get' applied with
26823         //     the store 'extra data'
26824         // and the body get's added to the
26825         //      roo-name="data" node?
26826         //      <span class='roo-tpl-{name}'></span> ?????
26827         
26828         
26829         
26830         this.clearSelections();
26831         this.el.update("");
26832         var html = [];
26833         var records = this.store.getRange();
26834         if(records.length < 1) {
26835             
26836             // is this valid??  = should it render a template??
26837             
26838             this.el.update(this.emptyText);
26839             return;
26840         }
26841         var el = this.el;
26842         if (this.dataName) {
26843             this.el.update(t.apply(this.store.meta)); //????
26844             el = this.el.child('.roo-tpl-' + this.dataName);
26845         }
26846         
26847         for(var i = 0, len = records.length; i < len; i++){
26848             var data = this.prepareData(records[i].data, i, records[i]);
26849             this.fireEvent("preparedata", this, data, i, records[i]);
26850             
26851             var d = Roo.apply({}, data);
26852             
26853             if(this.tickable){
26854                 Roo.apply(d, {'roo-id' : Roo.id()});
26855                 
26856                 var _this = this;
26857             
26858                 Roo.each(this.parent.item, function(item){
26859                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26860                         return;
26861                     }
26862                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26863                 });
26864             }
26865             
26866             html[html.length] = Roo.util.Format.trim(
26867                 this.dataName ?
26868                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26869                     t.apply(d)
26870             );
26871         }
26872         
26873         
26874         
26875         el.update(html.join(""));
26876         this.nodes = el.dom.childNodes;
26877         this.updateIndexes(0);
26878     },
26879     
26880
26881     /**
26882      * Function to override to reformat the data that is sent to
26883      * the template for each node.
26884      * DEPRICATED - use the preparedata event handler.
26885      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26886      * a JSON object for an UpdateManager bound view).
26887      */
26888     prepareData : function(data, index, record)
26889     {
26890         this.fireEvent("preparedata", this, data, index, record);
26891         return data;
26892     },
26893
26894     onUpdate : function(ds, record){
26895         // Roo.log('on update');   
26896         this.clearSelections();
26897         var index = this.store.indexOf(record);
26898         var n = this.nodes[index];
26899         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26900         n.parentNode.removeChild(n);
26901         this.updateIndexes(index, index);
26902     },
26903
26904     
26905     
26906 // --------- FIXME     
26907     onAdd : function(ds, records, index)
26908     {
26909         //Roo.log(['on Add', ds, records, index] );        
26910         this.clearSelections();
26911         if(this.nodes.length == 0){
26912             this.refresh();
26913             return;
26914         }
26915         var n = this.nodes[index];
26916         for(var i = 0, len = records.length; i < len; i++){
26917             var d = this.prepareData(records[i].data, i, records[i]);
26918             if(n){
26919                 this.tpl.insertBefore(n, d);
26920             }else{
26921                 
26922                 this.tpl.append(this.el, d);
26923             }
26924         }
26925         this.updateIndexes(index);
26926     },
26927
26928     onRemove : function(ds, record, index){
26929        // Roo.log('onRemove');
26930         this.clearSelections();
26931         var el = this.dataName  ?
26932             this.el.child('.roo-tpl-' + this.dataName) :
26933             this.el; 
26934         
26935         el.dom.removeChild(this.nodes[index]);
26936         this.updateIndexes(index);
26937     },
26938
26939     /**
26940      * Refresh an individual node.
26941      * @param {Number} index
26942      */
26943     refreshNode : function(index){
26944         this.onUpdate(this.store, this.store.getAt(index));
26945     },
26946
26947     updateIndexes : function(startIndex, endIndex){
26948         var ns = this.nodes;
26949         startIndex = startIndex || 0;
26950         endIndex = endIndex || ns.length - 1;
26951         for(var i = startIndex; i <= endIndex; i++){
26952             ns[i].nodeIndex = i;
26953         }
26954     },
26955
26956     /**
26957      * Changes the data store this view uses and refresh the view.
26958      * @param {Store} store
26959      */
26960     setStore : function(store, initial){
26961         if(!initial && this.store){
26962             this.store.un("datachanged", this.refresh);
26963             this.store.un("add", this.onAdd);
26964             this.store.un("remove", this.onRemove);
26965             this.store.un("update", this.onUpdate);
26966             this.store.un("clear", this.refresh);
26967             this.store.un("beforeload", this.onBeforeLoad);
26968             this.store.un("load", this.onLoad);
26969             this.store.un("loadexception", this.onLoad);
26970         }
26971         if(store){
26972           
26973             store.on("datachanged", this.refresh, this);
26974             store.on("add", this.onAdd, this);
26975             store.on("remove", this.onRemove, this);
26976             store.on("update", this.onUpdate, this);
26977             store.on("clear", this.refresh, this);
26978             store.on("beforeload", this.onBeforeLoad, this);
26979             store.on("load", this.onLoad, this);
26980             store.on("loadexception", this.onLoad, this);
26981         }
26982         
26983         if(store){
26984             this.refresh();
26985         }
26986     },
26987     /**
26988      * onbeforeLoad - masks the loading area.
26989      *
26990      */
26991     onBeforeLoad : function(store,opts)
26992     {
26993          //Roo.log('onBeforeLoad');   
26994         if (!opts.add) {
26995             this.el.update("");
26996         }
26997         this.el.mask(this.mask ? this.mask : "Loading" ); 
26998     },
26999     onLoad : function ()
27000     {
27001         this.el.unmask();
27002     },
27003     
27004
27005     /**
27006      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27007      * @param {HTMLElement} node
27008      * @return {HTMLElement} The template node
27009      */
27010     findItemFromChild : function(node){
27011         var el = this.dataName  ?
27012             this.el.child('.roo-tpl-' + this.dataName,true) :
27013             this.el.dom; 
27014         
27015         if(!node || node.parentNode == el){
27016                     return node;
27017             }
27018             var p = node.parentNode;
27019             while(p && p != el){
27020             if(p.parentNode == el){
27021                 return p;
27022             }
27023             p = p.parentNode;
27024         }
27025             return null;
27026     },
27027
27028     /** @ignore */
27029     onClick : function(e){
27030         var item = this.findItemFromChild(e.getTarget());
27031         if(item){
27032             var index = this.indexOf(item);
27033             if(this.onItemClick(item, index, e) !== false){
27034                 this.fireEvent("click", this, index, item, e);
27035             }
27036         }else{
27037             this.clearSelections();
27038         }
27039     },
27040
27041     /** @ignore */
27042     onContextMenu : function(e){
27043         var item = this.findItemFromChild(e.getTarget());
27044         if(item){
27045             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27046         }
27047     },
27048
27049     /** @ignore */
27050     onDblClick : function(e){
27051         var item = this.findItemFromChild(e.getTarget());
27052         if(item){
27053             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27054         }
27055     },
27056
27057     onItemClick : function(item, index, e)
27058     {
27059         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27060             return false;
27061         }
27062         if (this.toggleSelect) {
27063             var m = this.isSelected(item) ? 'unselect' : 'select';
27064             //Roo.log(m);
27065             var _t = this;
27066             _t[m](item, true, false);
27067             return true;
27068         }
27069         if(this.multiSelect || this.singleSelect){
27070             if(this.multiSelect && e.shiftKey && this.lastSelection){
27071                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27072             }else{
27073                 this.select(item, this.multiSelect && e.ctrlKey);
27074                 this.lastSelection = item;
27075             }
27076             
27077             if(!this.tickable){
27078                 e.preventDefault();
27079             }
27080             
27081         }
27082         return true;
27083     },
27084
27085     /**
27086      * Get the number of selected nodes.
27087      * @return {Number}
27088      */
27089     getSelectionCount : function(){
27090         return this.selections.length;
27091     },
27092
27093     /**
27094      * Get the currently selected nodes.
27095      * @return {Array} An array of HTMLElements
27096      */
27097     getSelectedNodes : function(){
27098         return this.selections;
27099     },
27100
27101     /**
27102      * Get the indexes of the selected nodes.
27103      * @return {Array}
27104      */
27105     getSelectedIndexes : function(){
27106         var indexes = [], s = this.selections;
27107         for(var i = 0, len = s.length; i < len; i++){
27108             indexes.push(s[i].nodeIndex);
27109         }
27110         return indexes;
27111     },
27112
27113     /**
27114      * Clear all selections
27115      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27116      */
27117     clearSelections : function(suppressEvent){
27118         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27119             this.cmp.elements = this.selections;
27120             this.cmp.removeClass(this.selectedClass);
27121             this.selections = [];
27122             if(!suppressEvent){
27123                 this.fireEvent("selectionchange", this, this.selections);
27124             }
27125         }
27126     },
27127
27128     /**
27129      * Returns true if the passed node is selected
27130      * @param {HTMLElement/Number} node The node or node index
27131      * @return {Boolean}
27132      */
27133     isSelected : function(node){
27134         var s = this.selections;
27135         if(s.length < 1){
27136             return false;
27137         }
27138         node = this.getNode(node);
27139         return s.indexOf(node) !== -1;
27140     },
27141
27142     /**
27143      * Selects nodes.
27144      * @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
27145      * @param {Boolean} keepExisting (optional) true to keep existing selections
27146      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27147      */
27148     select : function(nodeInfo, keepExisting, suppressEvent){
27149         if(nodeInfo instanceof Array){
27150             if(!keepExisting){
27151                 this.clearSelections(true);
27152             }
27153             for(var i = 0, len = nodeInfo.length; i < len; i++){
27154                 this.select(nodeInfo[i], true, true);
27155             }
27156             return;
27157         } 
27158         var node = this.getNode(nodeInfo);
27159         if(!node || this.isSelected(node)){
27160             return; // already selected.
27161         }
27162         if(!keepExisting){
27163             this.clearSelections(true);
27164         }
27165         
27166         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27167             Roo.fly(node).addClass(this.selectedClass);
27168             this.selections.push(node);
27169             if(!suppressEvent){
27170                 this.fireEvent("selectionchange", this, this.selections);
27171             }
27172         }
27173         
27174         
27175     },
27176       /**
27177      * Unselects nodes.
27178      * @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
27179      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27180      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27181      */
27182     unselect : function(nodeInfo, keepExisting, suppressEvent)
27183     {
27184         if(nodeInfo instanceof Array){
27185             Roo.each(this.selections, function(s) {
27186                 this.unselect(s, nodeInfo);
27187             }, this);
27188             return;
27189         }
27190         var node = this.getNode(nodeInfo);
27191         if(!node || !this.isSelected(node)){
27192             //Roo.log("not selected");
27193             return; // not selected.
27194         }
27195         // fireevent???
27196         var ns = [];
27197         Roo.each(this.selections, function(s) {
27198             if (s == node ) {
27199                 Roo.fly(node).removeClass(this.selectedClass);
27200
27201                 return;
27202             }
27203             ns.push(s);
27204         },this);
27205         
27206         this.selections= ns;
27207         this.fireEvent("selectionchange", this, this.selections);
27208     },
27209
27210     /**
27211      * Gets a template node.
27212      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27213      * @return {HTMLElement} The node or null if it wasn't found
27214      */
27215     getNode : function(nodeInfo){
27216         if(typeof nodeInfo == "string"){
27217             return document.getElementById(nodeInfo);
27218         }else if(typeof nodeInfo == "number"){
27219             return this.nodes[nodeInfo];
27220         }
27221         return nodeInfo;
27222     },
27223
27224     /**
27225      * Gets a range template nodes.
27226      * @param {Number} startIndex
27227      * @param {Number} endIndex
27228      * @return {Array} An array of nodes
27229      */
27230     getNodes : function(start, end){
27231         var ns = this.nodes;
27232         start = start || 0;
27233         end = typeof end == "undefined" ? ns.length - 1 : end;
27234         var nodes = [];
27235         if(start <= end){
27236             for(var i = start; i <= end; i++){
27237                 nodes.push(ns[i]);
27238             }
27239         } else{
27240             for(var i = start; i >= end; i--){
27241                 nodes.push(ns[i]);
27242             }
27243         }
27244         return nodes;
27245     },
27246
27247     /**
27248      * Finds the index of the passed node
27249      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27250      * @return {Number} The index of the node or -1
27251      */
27252     indexOf : function(node){
27253         node = this.getNode(node);
27254         if(typeof node.nodeIndex == "number"){
27255             return node.nodeIndex;
27256         }
27257         var ns = this.nodes;
27258         for(var i = 0, len = ns.length; i < len; i++){
27259             if(ns[i] == node){
27260                 return i;
27261             }
27262         }
27263         return -1;
27264     }
27265 });
27266 /*
27267  * Based on:
27268  * Ext JS Library 1.1.1
27269  * Copyright(c) 2006-2007, Ext JS, LLC.
27270  *
27271  * Originally Released Under LGPL - original licence link has changed is not relivant.
27272  *
27273  * Fork - LGPL
27274  * <script type="text/javascript">
27275  */
27276
27277 /**
27278  * @class Roo.JsonView
27279  * @extends Roo.View
27280  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27281 <pre><code>
27282 var view = new Roo.JsonView({
27283     container: "my-element",
27284     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27285     multiSelect: true, 
27286     jsonRoot: "data" 
27287 });
27288
27289 // listen for node click?
27290 view.on("click", function(vw, index, node, e){
27291     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27292 });
27293
27294 // direct load of JSON data
27295 view.load("foobar.php");
27296
27297 // Example from my blog list
27298 var tpl = new Roo.Template(
27299     '&lt;div class="entry"&gt;' +
27300     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27301     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27302     "&lt;/div&gt;&lt;hr /&gt;"
27303 );
27304
27305 var moreView = new Roo.JsonView({
27306     container :  "entry-list", 
27307     template : tpl,
27308     jsonRoot: "posts"
27309 });
27310 moreView.on("beforerender", this.sortEntries, this);
27311 moreView.load({
27312     url: "/blog/get-posts.php",
27313     params: "allposts=true",
27314     text: "Loading Blog Entries..."
27315 });
27316 </code></pre>
27317
27318 * Note: old code is supported with arguments : (container, template, config)
27319
27320
27321  * @constructor
27322  * Create a new JsonView
27323  * 
27324  * @param {Object} config The config object
27325  * 
27326  */
27327 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27328     
27329     
27330     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27331
27332     var um = this.el.getUpdateManager();
27333     um.setRenderer(this);
27334     um.on("update", this.onLoad, this);
27335     um.on("failure", this.onLoadException, this);
27336
27337     /**
27338      * @event beforerender
27339      * Fires before rendering of the downloaded JSON data.
27340      * @param {Roo.JsonView} this
27341      * @param {Object} data The JSON data loaded
27342      */
27343     /**
27344      * @event load
27345      * Fires when data is loaded.
27346      * @param {Roo.JsonView} this
27347      * @param {Object} data The JSON data loaded
27348      * @param {Object} response The raw Connect response object
27349      */
27350     /**
27351      * @event loadexception
27352      * Fires when loading fails.
27353      * @param {Roo.JsonView} this
27354      * @param {Object} response The raw Connect response object
27355      */
27356     this.addEvents({
27357         'beforerender' : true,
27358         'load' : true,
27359         'loadexception' : true
27360     });
27361 };
27362 Roo.extend(Roo.JsonView, Roo.View, {
27363     /**
27364      * @type {String} The root property in the loaded JSON object that contains the data
27365      */
27366     jsonRoot : "",
27367
27368     /**
27369      * Refreshes the view.
27370      */
27371     refresh : function(){
27372         this.clearSelections();
27373         this.el.update("");
27374         var html = [];
27375         var o = this.jsonData;
27376         if(o && o.length > 0){
27377             for(var i = 0, len = o.length; i < len; i++){
27378                 var data = this.prepareData(o[i], i, o);
27379                 html[html.length] = this.tpl.apply(data);
27380             }
27381         }else{
27382             html.push(this.emptyText);
27383         }
27384         this.el.update(html.join(""));
27385         this.nodes = this.el.dom.childNodes;
27386         this.updateIndexes(0);
27387     },
27388
27389     /**
27390      * 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.
27391      * @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:
27392      <pre><code>
27393      view.load({
27394          url: "your-url.php",
27395          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27396          callback: yourFunction,
27397          scope: yourObject, //(optional scope)
27398          discardUrl: false,
27399          nocache: false,
27400          text: "Loading...",
27401          timeout: 30,
27402          scripts: false
27403      });
27404      </code></pre>
27405      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27406      * 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.
27407      * @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}
27408      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27409      * @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.
27410      */
27411     load : function(){
27412         var um = this.el.getUpdateManager();
27413         um.update.apply(um, arguments);
27414     },
27415
27416     // note - render is a standard framework call...
27417     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27418     render : function(el, response){
27419         
27420         this.clearSelections();
27421         this.el.update("");
27422         var o;
27423         try{
27424             if (response != '') {
27425                 o = Roo.util.JSON.decode(response.responseText);
27426                 if(this.jsonRoot){
27427                     
27428                     o = o[this.jsonRoot];
27429                 }
27430             }
27431         } catch(e){
27432         }
27433         /**
27434          * The current JSON data or null
27435          */
27436         this.jsonData = o;
27437         this.beforeRender();
27438         this.refresh();
27439     },
27440
27441 /**
27442  * Get the number of records in the current JSON dataset
27443  * @return {Number}
27444  */
27445     getCount : function(){
27446         return this.jsonData ? this.jsonData.length : 0;
27447     },
27448
27449 /**
27450  * Returns the JSON object for the specified node(s)
27451  * @param {HTMLElement/Array} node The node or an array of nodes
27452  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27453  * you get the JSON object for the node
27454  */
27455     getNodeData : function(node){
27456         if(node instanceof Array){
27457             var data = [];
27458             for(var i = 0, len = node.length; i < len; i++){
27459                 data.push(this.getNodeData(node[i]));
27460             }
27461             return data;
27462         }
27463         return this.jsonData[this.indexOf(node)] || null;
27464     },
27465
27466     beforeRender : function(){
27467         this.snapshot = this.jsonData;
27468         if(this.sortInfo){
27469             this.sort.apply(this, this.sortInfo);
27470         }
27471         this.fireEvent("beforerender", this, this.jsonData);
27472     },
27473
27474     onLoad : function(el, o){
27475         this.fireEvent("load", this, this.jsonData, o);
27476     },
27477
27478     onLoadException : function(el, o){
27479         this.fireEvent("loadexception", this, o);
27480     },
27481
27482 /**
27483  * Filter the data by a specific property.
27484  * @param {String} property A property on your JSON objects
27485  * @param {String/RegExp} value Either string that the property values
27486  * should start with, or a RegExp to test against the property
27487  */
27488     filter : function(property, value){
27489         if(this.jsonData){
27490             var data = [];
27491             var ss = this.snapshot;
27492             if(typeof value == "string"){
27493                 var vlen = value.length;
27494                 if(vlen == 0){
27495                     this.clearFilter();
27496                     return;
27497                 }
27498                 value = value.toLowerCase();
27499                 for(var i = 0, len = ss.length; i < len; i++){
27500                     var o = ss[i];
27501                     if(o[property].substr(0, vlen).toLowerCase() == value){
27502                         data.push(o);
27503                     }
27504                 }
27505             } else if(value.exec){ // regex?
27506                 for(var i = 0, len = ss.length; i < len; i++){
27507                     var o = ss[i];
27508                     if(value.test(o[property])){
27509                         data.push(o);
27510                     }
27511                 }
27512             } else{
27513                 return;
27514             }
27515             this.jsonData = data;
27516             this.refresh();
27517         }
27518     },
27519
27520 /**
27521  * Filter by a function. The passed function will be called with each
27522  * object in the current dataset. If the function returns true the value is kept,
27523  * otherwise it is filtered.
27524  * @param {Function} fn
27525  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27526  */
27527     filterBy : function(fn, scope){
27528         if(this.jsonData){
27529             var data = [];
27530             var ss = this.snapshot;
27531             for(var i = 0, len = ss.length; i < len; i++){
27532                 var o = ss[i];
27533                 if(fn.call(scope || this, o)){
27534                     data.push(o);
27535                 }
27536             }
27537             this.jsonData = data;
27538             this.refresh();
27539         }
27540     },
27541
27542 /**
27543  * Clears the current filter.
27544  */
27545     clearFilter : function(){
27546         if(this.snapshot && this.jsonData != this.snapshot){
27547             this.jsonData = this.snapshot;
27548             this.refresh();
27549         }
27550     },
27551
27552
27553 /**
27554  * Sorts the data for this view and refreshes it.
27555  * @param {String} property A property on your JSON objects to sort on
27556  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27557  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27558  */
27559     sort : function(property, dir, sortType){
27560         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27561         if(this.jsonData){
27562             var p = property;
27563             var dsc = dir && dir.toLowerCase() == "desc";
27564             var f = function(o1, o2){
27565                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27566                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27567                 ;
27568                 if(v1 < v2){
27569                     return dsc ? +1 : -1;
27570                 } else if(v1 > v2){
27571                     return dsc ? -1 : +1;
27572                 } else{
27573                     return 0;
27574                 }
27575             };
27576             this.jsonData.sort(f);
27577             this.refresh();
27578             if(this.jsonData != this.snapshot){
27579                 this.snapshot.sort(f);
27580             }
27581         }
27582     }
27583 });/*
27584  * Based on:
27585  * Ext JS Library 1.1.1
27586  * Copyright(c) 2006-2007, Ext JS, LLC.
27587  *
27588  * Originally Released Under LGPL - original licence link has changed is not relivant.
27589  *
27590  * Fork - LGPL
27591  * <script type="text/javascript">
27592  */
27593  
27594
27595 /**
27596  * @class Roo.ColorPalette
27597  * @extends Roo.Component
27598  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27599  * Here's an example of typical usage:
27600  * <pre><code>
27601 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27602 cp.render('my-div');
27603
27604 cp.on('select', function(palette, selColor){
27605     // do something with selColor
27606 });
27607 </code></pre>
27608  * @constructor
27609  * Create a new ColorPalette
27610  * @param {Object} config The config object
27611  */
27612 Roo.ColorPalette = function(config){
27613     Roo.ColorPalette.superclass.constructor.call(this, config);
27614     this.addEvents({
27615         /**
27616              * @event select
27617              * Fires when a color is selected
27618              * @param {ColorPalette} this
27619              * @param {String} color The 6-digit color hex code (without the # symbol)
27620              */
27621         select: true
27622     });
27623
27624     if(this.handler){
27625         this.on("select", this.handler, this.scope, true);
27626     }
27627 };
27628 Roo.extend(Roo.ColorPalette, Roo.Component, {
27629     /**
27630      * @cfg {String} itemCls
27631      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27632      */
27633     itemCls : "x-color-palette",
27634     /**
27635      * @cfg {String} value
27636      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27637      * the hex codes are case-sensitive.
27638      */
27639     value : null,
27640     clickEvent:'click',
27641     // private
27642     ctype: "Roo.ColorPalette",
27643
27644     /**
27645      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27646      */
27647     allowReselect : false,
27648
27649     /**
27650      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27651      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27652      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27653      * of colors with the width setting until the box is symmetrical.</p>
27654      * <p>You can override individual colors if needed:</p>
27655      * <pre><code>
27656 var cp = new Roo.ColorPalette();
27657 cp.colors[0] = "FF0000";  // change the first box to red
27658 </code></pre>
27659
27660 Or you can provide a custom array of your own for complete control:
27661 <pre><code>
27662 var cp = new Roo.ColorPalette();
27663 cp.colors = ["000000", "993300", "333300"];
27664 </code></pre>
27665      * @type Array
27666      */
27667     colors : [
27668         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27669         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27670         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27671         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27672         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27673     ],
27674
27675     // private
27676     onRender : function(container, position){
27677         var t = new Roo.MasterTemplate(
27678             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27679         );
27680         var c = this.colors;
27681         for(var i = 0, len = c.length; i < len; i++){
27682             t.add([c[i]]);
27683         }
27684         var el = document.createElement("div");
27685         el.className = this.itemCls;
27686         t.overwrite(el);
27687         container.dom.insertBefore(el, position);
27688         this.el = Roo.get(el);
27689         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27690         if(this.clickEvent != 'click'){
27691             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27692         }
27693     },
27694
27695     // private
27696     afterRender : function(){
27697         Roo.ColorPalette.superclass.afterRender.call(this);
27698         if(this.value){
27699             var s = this.value;
27700             this.value = null;
27701             this.select(s);
27702         }
27703     },
27704
27705     // private
27706     handleClick : function(e, t){
27707         e.preventDefault();
27708         if(!this.disabled){
27709             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27710             this.select(c.toUpperCase());
27711         }
27712     },
27713
27714     /**
27715      * Selects the specified color in the palette (fires the select event)
27716      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27717      */
27718     select : function(color){
27719         color = color.replace("#", "");
27720         if(color != this.value || this.allowReselect){
27721             var el = this.el;
27722             if(this.value){
27723                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27724             }
27725             el.child("a.color-"+color).addClass("x-color-palette-sel");
27726             this.value = color;
27727             this.fireEvent("select", this, color);
27728         }
27729     }
27730 });/*
27731  * Based on:
27732  * Ext JS Library 1.1.1
27733  * Copyright(c) 2006-2007, Ext JS, LLC.
27734  *
27735  * Originally Released Under LGPL - original licence link has changed is not relivant.
27736  *
27737  * Fork - LGPL
27738  * <script type="text/javascript">
27739  */
27740  
27741 /**
27742  * @class Roo.DatePicker
27743  * @extends Roo.Component
27744  * Simple date picker class.
27745  * @constructor
27746  * Create a new DatePicker
27747  * @param {Object} config The config object
27748  */
27749 Roo.DatePicker = function(config){
27750     Roo.DatePicker.superclass.constructor.call(this, config);
27751
27752     this.value = config && config.value ?
27753                  config.value.clearTime() : new Date().clearTime();
27754
27755     this.addEvents({
27756         /**
27757              * @event select
27758              * Fires when a date is selected
27759              * @param {DatePicker} this
27760              * @param {Date} date The selected date
27761              */
27762         'select': true,
27763         /**
27764              * @event monthchange
27765              * Fires when the displayed month changes 
27766              * @param {DatePicker} this
27767              * @param {Date} date The selected month
27768              */
27769         'monthchange': true
27770     });
27771
27772     if(this.handler){
27773         this.on("select", this.handler,  this.scope || this);
27774     }
27775     // build the disabledDatesRE
27776     if(!this.disabledDatesRE && this.disabledDates){
27777         var dd = this.disabledDates;
27778         var re = "(?:";
27779         for(var i = 0; i < dd.length; i++){
27780             re += dd[i];
27781             if(i != dd.length-1) {
27782                 re += "|";
27783             }
27784         }
27785         this.disabledDatesRE = new RegExp(re + ")");
27786     }
27787 };
27788
27789 Roo.extend(Roo.DatePicker, Roo.Component, {
27790     /**
27791      * @cfg {String} todayText
27792      * The text to display on the button that selects the current date (defaults to "Today")
27793      */
27794     todayText : "Today",
27795     /**
27796      * @cfg {String} okText
27797      * The text to display on the ok button
27798      */
27799     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27800     /**
27801      * @cfg {String} cancelText
27802      * The text to display on the cancel button
27803      */
27804     cancelText : "Cancel",
27805     /**
27806      * @cfg {String} todayTip
27807      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27808      */
27809     todayTip : "{0} (Spacebar)",
27810     /**
27811      * @cfg {Date} minDate
27812      * Minimum allowable date (JavaScript date object, defaults to null)
27813      */
27814     minDate : null,
27815     /**
27816      * @cfg {Date} maxDate
27817      * Maximum allowable date (JavaScript date object, defaults to null)
27818      */
27819     maxDate : null,
27820     /**
27821      * @cfg {String} minText
27822      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27823      */
27824     minText : "This date is before the minimum date",
27825     /**
27826      * @cfg {String} maxText
27827      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27828      */
27829     maxText : "This date is after the maximum date",
27830     /**
27831      * @cfg {String} format
27832      * The default date format string which can be overriden for localization support.  The format must be
27833      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27834      */
27835     format : "m/d/y",
27836     /**
27837      * @cfg {Array} disabledDays
27838      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27839      */
27840     disabledDays : null,
27841     /**
27842      * @cfg {String} disabledDaysText
27843      * The tooltip to display when the date falls on a disabled day (defaults to "")
27844      */
27845     disabledDaysText : "",
27846     /**
27847      * @cfg {RegExp} disabledDatesRE
27848      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27849      */
27850     disabledDatesRE : null,
27851     /**
27852      * @cfg {String} disabledDatesText
27853      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27854      */
27855     disabledDatesText : "",
27856     /**
27857      * @cfg {Boolean} constrainToViewport
27858      * True to constrain the date picker to the viewport (defaults to true)
27859      */
27860     constrainToViewport : true,
27861     /**
27862      * @cfg {Array} monthNames
27863      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27864      */
27865     monthNames : Date.monthNames,
27866     /**
27867      * @cfg {Array} dayNames
27868      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27869      */
27870     dayNames : Date.dayNames,
27871     /**
27872      * @cfg {String} nextText
27873      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27874      */
27875     nextText: 'Next Month (Control+Right)',
27876     /**
27877      * @cfg {String} prevText
27878      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27879      */
27880     prevText: 'Previous Month (Control+Left)',
27881     /**
27882      * @cfg {String} monthYearText
27883      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27884      */
27885     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27886     /**
27887      * @cfg {Number} startDay
27888      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27889      */
27890     startDay : 0,
27891     /**
27892      * @cfg {Bool} showClear
27893      * Show a clear button (usefull for date form elements that can be blank.)
27894      */
27895     
27896     showClear: false,
27897     
27898     /**
27899      * Sets the value of the date field
27900      * @param {Date} value The date to set
27901      */
27902     setValue : function(value){
27903         var old = this.value;
27904         
27905         if (typeof(value) == 'string') {
27906          
27907             value = Date.parseDate(value, this.format);
27908         }
27909         if (!value) {
27910             value = new Date();
27911         }
27912         
27913         this.value = value.clearTime(true);
27914         if(this.el){
27915             this.update(this.value);
27916         }
27917     },
27918
27919     /**
27920      * Gets the current selected value of the date field
27921      * @return {Date} The selected date
27922      */
27923     getValue : function(){
27924         return this.value;
27925     },
27926
27927     // private
27928     focus : function(){
27929         if(this.el){
27930             this.update(this.activeDate);
27931         }
27932     },
27933
27934     // privateval
27935     onRender : function(container, position){
27936         
27937         var m = [
27938              '<table cellspacing="0">',
27939                 '<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>',
27940                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27941         var dn = this.dayNames;
27942         for(var i = 0; i < 7; i++){
27943             var d = this.startDay+i;
27944             if(d > 6){
27945                 d = d-7;
27946             }
27947             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27948         }
27949         m[m.length] = "</tr></thead><tbody><tr>";
27950         for(var i = 0; i < 42; i++) {
27951             if(i % 7 == 0 && i != 0){
27952                 m[m.length] = "</tr><tr>";
27953             }
27954             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27955         }
27956         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27957             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27958
27959         var el = document.createElement("div");
27960         el.className = "x-date-picker";
27961         el.innerHTML = m.join("");
27962
27963         container.dom.insertBefore(el, position);
27964
27965         this.el = Roo.get(el);
27966         this.eventEl = Roo.get(el.firstChild);
27967
27968         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27969             handler: this.showPrevMonth,
27970             scope: this,
27971             preventDefault:true,
27972             stopDefault:true
27973         });
27974
27975         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27976             handler: this.showNextMonth,
27977             scope: this,
27978             preventDefault:true,
27979             stopDefault:true
27980         });
27981
27982         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27983
27984         this.monthPicker = this.el.down('div.x-date-mp');
27985         this.monthPicker.enableDisplayMode('block');
27986         
27987         var kn = new Roo.KeyNav(this.eventEl, {
27988             "left" : function(e){
27989                 e.ctrlKey ?
27990                     this.showPrevMonth() :
27991                     this.update(this.activeDate.add("d", -1));
27992             },
27993
27994             "right" : function(e){
27995                 e.ctrlKey ?
27996                     this.showNextMonth() :
27997                     this.update(this.activeDate.add("d", 1));
27998             },
27999
28000             "up" : function(e){
28001                 e.ctrlKey ?
28002                     this.showNextYear() :
28003                     this.update(this.activeDate.add("d", -7));
28004             },
28005
28006             "down" : function(e){
28007                 e.ctrlKey ?
28008                     this.showPrevYear() :
28009                     this.update(this.activeDate.add("d", 7));
28010             },
28011
28012             "pageUp" : function(e){
28013                 this.showNextMonth();
28014             },
28015
28016             "pageDown" : function(e){
28017                 this.showPrevMonth();
28018             },
28019
28020             "enter" : function(e){
28021                 e.stopPropagation();
28022                 return true;
28023             },
28024
28025             scope : this
28026         });
28027
28028         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28029
28030         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28031
28032         this.el.unselectable();
28033         
28034         this.cells = this.el.select("table.x-date-inner tbody td");
28035         this.textNodes = this.el.query("table.x-date-inner tbody span");
28036
28037         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28038             text: "&#160;",
28039             tooltip: this.monthYearText
28040         });
28041
28042         this.mbtn.on('click', this.showMonthPicker, this);
28043         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28044
28045
28046         var today = (new Date()).dateFormat(this.format);
28047         
28048         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28049         if (this.showClear) {
28050             baseTb.add( new Roo.Toolbar.Fill());
28051         }
28052         baseTb.add({
28053             text: String.format(this.todayText, today),
28054             tooltip: String.format(this.todayTip, today),
28055             handler: this.selectToday,
28056             scope: this
28057         });
28058         
28059         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28060             
28061         //});
28062         if (this.showClear) {
28063             
28064             baseTb.add( new Roo.Toolbar.Fill());
28065             baseTb.add({
28066                 text: '&#160;',
28067                 cls: 'x-btn-icon x-btn-clear',
28068                 handler: function() {
28069                     //this.value = '';
28070                     this.fireEvent("select", this, '');
28071                 },
28072                 scope: this
28073             });
28074         }
28075         
28076         
28077         if(Roo.isIE){
28078             this.el.repaint();
28079         }
28080         this.update(this.value);
28081     },
28082
28083     createMonthPicker : function(){
28084         if(!this.monthPicker.dom.firstChild){
28085             var buf = ['<table border="0" cellspacing="0">'];
28086             for(var i = 0; i < 6; i++){
28087                 buf.push(
28088                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28089                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28090                     i == 0 ?
28091                     '<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>' :
28092                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28093                 );
28094             }
28095             buf.push(
28096                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28097                     this.okText,
28098                     '</button><button type="button" class="x-date-mp-cancel">',
28099                     this.cancelText,
28100                     '</button></td></tr>',
28101                 '</table>'
28102             );
28103             this.monthPicker.update(buf.join(''));
28104             this.monthPicker.on('click', this.onMonthClick, this);
28105             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28106
28107             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28108             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28109
28110             this.mpMonths.each(function(m, a, i){
28111                 i += 1;
28112                 if((i%2) == 0){
28113                     m.dom.xmonth = 5 + Math.round(i * .5);
28114                 }else{
28115                     m.dom.xmonth = Math.round((i-1) * .5);
28116                 }
28117             });
28118         }
28119     },
28120
28121     showMonthPicker : function(){
28122         this.createMonthPicker();
28123         var size = this.el.getSize();
28124         this.monthPicker.setSize(size);
28125         this.monthPicker.child('table').setSize(size);
28126
28127         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28128         this.updateMPMonth(this.mpSelMonth);
28129         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28130         this.updateMPYear(this.mpSelYear);
28131
28132         this.monthPicker.slideIn('t', {duration:.2});
28133     },
28134
28135     updateMPYear : function(y){
28136         this.mpyear = y;
28137         var ys = this.mpYears.elements;
28138         for(var i = 1; i <= 10; i++){
28139             var td = ys[i-1], y2;
28140             if((i%2) == 0){
28141                 y2 = y + Math.round(i * .5);
28142                 td.firstChild.innerHTML = y2;
28143                 td.xyear = y2;
28144             }else{
28145                 y2 = y - (5-Math.round(i * .5));
28146                 td.firstChild.innerHTML = y2;
28147                 td.xyear = y2;
28148             }
28149             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28150         }
28151     },
28152
28153     updateMPMonth : function(sm){
28154         this.mpMonths.each(function(m, a, i){
28155             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28156         });
28157     },
28158
28159     selectMPMonth: function(m){
28160         
28161     },
28162
28163     onMonthClick : function(e, t){
28164         e.stopEvent();
28165         var el = new Roo.Element(t), pn;
28166         if(el.is('button.x-date-mp-cancel')){
28167             this.hideMonthPicker();
28168         }
28169         else if(el.is('button.x-date-mp-ok')){
28170             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28171             this.hideMonthPicker();
28172         }
28173         else if(pn = el.up('td.x-date-mp-month', 2)){
28174             this.mpMonths.removeClass('x-date-mp-sel');
28175             pn.addClass('x-date-mp-sel');
28176             this.mpSelMonth = pn.dom.xmonth;
28177         }
28178         else if(pn = el.up('td.x-date-mp-year', 2)){
28179             this.mpYears.removeClass('x-date-mp-sel');
28180             pn.addClass('x-date-mp-sel');
28181             this.mpSelYear = pn.dom.xyear;
28182         }
28183         else if(el.is('a.x-date-mp-prev')){
28184             this.updateMPYear(this.mpyear-10);
28185         }
28186         else if(el.is('a.x-date-mp-next')){
28187             this.updateMPYear(this.mpyear+10);
28188         }
28189     },
28190
28191     onMonthDblClick : function(e, t){
28192         e.stopEvent();
28193         var el = new Roo.Element(t), pn;
28194         if(pn = el.up('td.x-date-mp-month', 2)){
28195             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28196             this.hideMonthPicker();
28197         }
28198         else if(pn = el.up('td.x-date-mp-year', 2)){
28199             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28200             this.hideMonthPicker();
28201         }
28202     },
28203
28204     hideMonthPicker : function(disableAnim){
28205         if(this.monthPicker){
28206             if(disableAnim === true){
28207                 this.monthPicker.hide();
28208             }else{
28209                 this.monthPicker.slideOut('t', {duration:.2});
28210             }
28211         }
28212     },
28213
28214     // private
28215     showPrevMonth : function(e){
28216         this.update(this.activeDate.add("mo", -1));
28217     },
28218
28219     // private
28220     showNextMonth : function(e){
28221         this.update(this.activeDate.add("mo", 1));
28222     },
28223
28224     // private
28225     showPrevYear : function(){
28226         this.update(this.activeDate.add("y", -1));
28227     },
28228
28229     // private
28230     showNextYear : function(){
28231         this.update(this.activeDate.add("y", 1));
28232     },
28233
28234     // private
28235     handleMouseWheel : function(e){
28236         var delta = e.getWheelDelta();
28237         if(delta > 0){
28238             this.showPrevMonth();
28239             e.stopEvent();
28240         } else if(delta < 0){
28241             this.showNextMonth();
28242             e.stopEvent();
28243         }
28244     },
28245
28246     // private
28247     handleDateClick : function(e, t){
28248         e.stopEvent();
28249         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28250             this.setValue(new Date(t.dateValue));
28251             this.fireEvent("select", this, this.value);
28252         }
28253     },
28254
28255     // private
28256     selectToday : function(){
28257         this.setValue(new Date().clearTime());
28258         this.fireEvent("select", this, this.value);
28259     },
28260
28261     // private
28262     update : function(date)
28263     {
28264         var vd = this.activeDate;
28265         this.activeDate = date;
28266         if(vd && this.el){
28267             var t = date.getTime();
28268             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28269                 this.cells.removeClass("x-date-selected");
28270                 this.cells.each(function(c){
28271                    if(c.dom.firstChild.dateValue == t){
28272                        c.addClass("x-date-selected");
28273                        setTimeout(function(){
28274                             try{c.dom.firstChild.focus();}catch(e){}
28275                        }, 50);
28276                        return false;
28277                    }
28278                 });
28279                 return;
28280             }
28281         }
28282         
28283         var days = date.getDaysInMonth();
28284         var firstOfMonth = date.getFirstDateOfMonth();
28285         var startingPos = firstOfMonth.getDay()-this.startDay;
28286
28287         if(startingPos <= this.startDay){
28288             startingPos += 7;
28289         }
28290
28291         var pm = date.add("mo", -1);
28292         var prevStart = pm.getDaysInMonth()-startingPos;
28293
28294         var cells = this.cells.elements;
28295         var textEls = this.textNodes;
28296         days += startingPos;
28297
28298         // convert everything to numbers so it's fast
28299         var day = 86400000;
28300         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28301         var today = new Date().clearTime().getTime();
28302         var sel = date.clearTime().getTime();
28303         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28304         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28305         var ddMatch = this.disabledDatesRE;
28306         var ddText = this.disabledDatesText;
28307         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28308         var ddaysText = this.disabledDaysText;
28309         var format = this.format;
28310
28311         var setCellClass = function(cal, cell){
28312             cell.title = "";
28313             var t = d.getTime();
28314             cell.firstChild.dateValue = t;
28315             if(t == today){
28316                 cell.className += " x-date-today";
28317                 cell.title = cal.todayText;
28318             }
28319             if(t == sel){
28320                 cell.className += " x-date-selected";
28321                 setTimeout(function(){
28322                     try{cell.firstChild.focus();}catch(e){}
28323                 }, 50);
28324             }
28325             // disabling
28326             if(t < min) {
28327                 cell.className = " x-date-disabled";
28328                 cell.title = cal.minText;
28329                 return;
28330             }
28331             if(t > max) {
28332                 cell.className = " x-date-disabled";
28333                 cell.title = cal.maxText;
28334                 return;
28335             }
28336             if(ddays){
28337                 if(ddays.indexOf(d.getDay()) != -1){
28338                     cell.title = ddaysText;
28339                     cell.className = " x-date-disabled";
28340                 }
28341             }
28342             if(ddMatch && format){
28343                 var fvalue = d.dateFormat(format);
28344                 if(ddMatch.test(fvalue)){
28345                     cell.title = ddText.replace("%0", fvalue);
28346                     cell.className = " x-date-disabled";
28347                 }
28348             }
28349         };
28350
28351         var i = 0;
28352         for(; i < startingPos; i++) {
28353             textEls[i].innerHTML = (++prevStart);
28354             d.setDate(d.getDate()+1);
28355             cells[i].className = "x-date-prevday";
28356             setCellClass(this, cells[i]);
28357         }
28358         for(; i < days; i++){
28359             intDay = i - startingPos + 1;
28360             textEls[i].innerHTML = (intDay);
28361             d.setDate(d.getDate()+1);
28362             cells[i].className = "x-date-active";
28363             setCellClass(this, cells[i]);
28364         }
28365         var extraDays = 0;
28366         for(; i < 42; i++) {
28367              textEls[i].innerHTML = (++extraDays);
28368              d.setDate(d.getDate()+1);
28369              cells[i].className = "x-date-nextday";
28370              setCellClass(this, cells[i]);
28371         }
28372
28373         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28374         this.fireEvent('monthchange', this, date);
28375         
28376         if(!this.internalRender){
28377             var main = this.el.dom.firstChild;
28378             var w = main.offsetWidth;
28379             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28380             Roo.fly(main).setWidth(w);
28381             this.internalRender = true;
28382             // opera does not respect the auto grow header center column
28383             // then, after it gets a width opera refuses to recalculate
28384             // without a second pass
28385             if(Roo.isOpera && !this.secondPass){
28386                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28387                 this.secondPass = true;
28388                 this.update.defer(10, this, [date]);
28389             }
28390         }
28391         
28392         
28393     }
28394 });        /*
28395  * Based on:
28396  * Ext JS Library 1.1.1
28397  * Copyright(c) 2006-2007, Ext JS, LLC.
28398  *
28399  * Originally Released Under LGPL - original licence link has changed is not relivant.
28400  *
28401  * Fork - LGPL
28402  * <script type="text/javascript">
28403  */
28404 /**
28405  * @class Roo.TabPanel
28406  * @extends Roo.util.Observable
28407  * A lightweight tab container.
28408  * <br><br>
28409  * Usage:
28410  * <pre><code>
28411 // basic tabs 1, built from existing content
28412 var tabs = new Roo.TabPanel("tabs1");
28413 tabs.addTab("script", "View Script");
28414 tabs.addTab("markup", "View Markup");
28415 tabs.activate("script");
28416
28417 // more advanced tabs, built from javascript
28418 var jtabs = new Roo.TabPanel("jtabs");
28419 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28420
28421 // set up the UpdateManager
28422 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28423 var updater = tab2.getUpdateManager();
28424 updater.setDefaultUrl("ajax1.htm");
28425 tab2.on('activate', updater.refresh, updater, true);
28426
28427 // Use setUrl for Ajax loading
28428 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28429 tab3.setUrl("ajax2.htm", null, true);
28430
28431 // Disabled tab
28432 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28433 tab4.disable();
28434
28435 jtabs.activate("jtabs-1");
28436  * </code></pre>
28437  * @constructor
28438  * Create a new TabPanel.
28439  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28440  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28441  */
28442 Roo.TabPanel = function(container, config){
28443     /**
28444     * The container element for this TabPanel.
28445     * @type Roo.Element
28446     */
28447     this.el = Roo.get(container, true);
28448     if(config){
28449         if(typeof config == "boolean"){
28450             this.tabPosition = config ? "bottom" : "top";
28451         }else{
28452             Roo.apply(this, config);
28453         }
28454     }
28455     if(this.tabPosition == "bottom"){
28456         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28457         this.el.addClass("x-tabs-bottom");
28458     }
28459     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28460     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28461     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28462     if(Roo.isIE){
28463         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28464     }
28465     if(this.tabPosition != "bottom"){
28466         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28467          * @type Roo.Element
28468          */
28469         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28470         this.el.addClass("x-tabs-top");
28471     }
28472     this.items = [];
28473
28474     this.bodyEl.setStyle("position", "relative");
28475
28476     this.active = null;
28477     this.activateDelegate = this.activate.createDelegate(this);
28478
28479     this.addEvents({
28480         /**
28481          * @event tabchange
28482          * Fires when the active tab changes
28483          * @param {Roo.TabPanel} this
28484          * @param {Roo.TabPanelItem} activePanel The new active tab
28485          */
28486         "tabchange": true,
28487         /**
28488          * @event beforetabchange
28489          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28490          * @param {Roo.TabPanel} this
28491          * @param {Object} e Set cancel to true on this object to cancel the tab change
28492          * @param {Roo.TabPanelItem} tab The tab being changed to
28493          */
28494         "beforetabchange" : true
28495     });
28496
28497     Roo.EventManager.onWindowResize(this.onResize, this);
28498     this.cpad = this.el.getPadding("lr");
28499     this.hiddenCount = 0;
28500
28501
28502     // toolbar on the tabbar support...
28503     if (this.toolbar) {
28504         var tcfg = this.toolbar;
28505         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28506         this.toolbar = new Roo.Toolbar(tcfg);
28507         if (Roo.isSafari) {
28508             var tbl = tcfg.container.child('table', true);
28509             tbl.setAttribute('width', '100%');
28510         }
28511         
28512     }
28513    
28514
28515
28516     Roo.TabPanel.superclass.constructor.call(this);
28517 };
28518
28519 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28520     /*
28521      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28522      */
28523     tabPosition : "top",
28524     /*
28525      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28526      */
28527     currentTabWidth : 0,
28528     /*
28529      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28530      */
28531     minTabWidth : 40,
28532     /*
28533      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28534      */
28535     maxTabWidth : 250,
28536     /*
28537      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28538      */
28539     preferredTabWidth : 175,
28540     /*
28541      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28542      */
28543     resizeTabs : false,
28544     /*
28545      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28546      */
28547     monitorResize : true,
28548     /*
28549      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28550      */
28551     toolbar : false,
28552
28553     /**
28554      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28555      * @param {String} id The id of the div to use <b>or create</b>
28556      * @param {String} text The text for the tab
28557      * @param {String} content (optional) Content to put in the TabPanelItem body
28558      * @param {Boolean} closable (optional) True to create a close icon on the tab
28559      * @return {Roo.TabPanelItem} The created TabPanelItem
28560      */
28561     addTab : function(id, text, content, closable){
28562         var item = new Roo.TabPanelItem(this, id, text, closable);
28563         this.addTabItem(item);
28564         if(content){
28565             item.setContent(content);
28566         }
28567         return item;
28568     },
28569
28570     /**
28571      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28572      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28573      * @return {Roo.TabPanelItem}
28574      */
28575     getTab : function(id){
28576         return this.items[id];
28577     },
28578
28579     /**
28580      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28581      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28582      */
28583     hideTab : function(id){
28584         var t = this.items[id];
28585         if(!t.isHidden()){
28586            t.setHidden(true);
28587            this.hiddenCount++;
28588            this.autoSizeTabs();
28589         }
28590     },
28591
28592     /**
28593      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28594      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28595      */
28596     unhideTab : function(id){
28597         var t = this.items[id];
28598         if(t.isHidden()){
28599            t.setHidden(false);
28600            this.hiddenCount--;
28601            this.autoSizeTabs();
28602         }
28603     },
28604
28605     /**
28606      * Adds an existing {@link Roo.TabPanelItem}.
28607      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28608      */
28609     addTabItem : function(item){
28610         this.items[item.id] = item;
28611         this.items.push(item);
28612         if(this.resizeTabs){
28613            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28614            this.autoSizeTabs();
28615         }else{
28616             item.autoSize();
28617         }
28618     },
28619
28620     /**
28621      * Removes a {@link Roo.TabPanelItem}.
28622      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28623      */
28624     removeTab : function(id){
28625         var items = this.items;
28626         var tab = items[id];
28627         if(!tab) { return; }
28628         var index = items.indexOf(tab);
28629         if(this.active == tab && items.length > 1){
28630             var newTab = this.getNextAvailable(index);
28631             if(newTab) {
28632                 newTab.activate();
28633             }
28634         }
28635         this.stripEl.dom.removeChild(tab.pnode.dom);
28636         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28637             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28638         }
28639         items.splice(index, 1);
28640         delete this.items[tab.id];
28641         tab.fireEvent("close", tab);
28642         tab.purgeListeners();
28643         this.autoSizeTabs();
28644     },
28645
28646     getNextAvailable : function(start){
28647         var items = this.items;
28648         var index = start;
28649         // look for a next tab that will slide over to
28650         // replace the one being removed
28651         while(index < items.length){
28652             var item = items[++index];
28653             if(item && !item.isHidden()){
28654                 return item;
28655             }
28656         }
28657         // if one isn't found select the previous tab (on the left)
28658         index = start;
28659         while(index >= 0){
28660             var item = items[--index];
28661             if(item && !item.isHidden()){
28662                 return item;
28663             }
28664         }
28665         return null;
28666     },
28667
28668     /**
28669      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28670      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28671      */
28672     disableTab : function(id){
28673         var tab = this.items[id];
28674         if(tab && this.active != tab){
28675             tab.disable();
28676         }
28677     },
28678
28679     /**
28680      * Enables a {@link Roo.TabPanelItem} that is disabled.
28681      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28682      */
28683     enableTab : function(id){
28684         var tab = this.items[id];
28685         tab.enable();
28686     },
28687
28688     /**
28689      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28690      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28691      * @return {Roo.TabPanelItem} The TabPanelItem.
28692      */
28693     activate : function(id){
28694         var tab = this.items[id];
28695         if(!tab){
28696             return null;
28697         }
28698         if(tab == this.active || tab.disabled){
28699             return tab;
28700         }
28701         var e = {};
28702         this.fireEvent("beforetabchange", this, e, tab);
28703         if(e.cancel !== true && !tab.disabled){
28704             if(this.active){
28705                 this.active.hide();
28706             }
28707             this.active = this.items[id];
28708             this.active.show();
28709             this.fireEvent("tabchange", this, this.active);
28710         }
28711         return tab;
28712     },
28713
28714     /**
28715      * Gets the active {@link Roo.TabPanelItem}.
28716      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28717      */
28718     getActiveTab : function(){
28719         return this.active;
28720     },
28721
28722     /**
28723      * Updates the tab body element to fit the height of the container element
28724      * for overflow scrolling
28725      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28726      */
28727     syncHeight : function(targetHeight){
28728         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28729         var bm = this.bodyEl.getMargins();
28730         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28731         this.bodyEl.setHeight(newHeight);
28732         return newHeight;
28733     },
28734
28735     onResize : function(){
28736         if(this.monitorResize){
28737             this.autoSizeTabs();
28738         }
28739     },
28740
28741     /**
28742      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28743      */
28744     beginUpdate : function(){
28745         this.updating = true;
28746     },
28747
28748     /**
28749      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28750      */
28751     endUpdate : function(){
28752         this.updating = false;
28753         this.autoSizeTabs();
28754     },
28755
28756     /**
28757      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28758      */
28759     autoSizeTabs : function(){
28760         var count = this.items.length;
28761         var vcount = count - this.hiddenCount;
28762         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28763             return;
28764         }
28765         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28766         var availWidth = Math.floor(w / vcount);
28767         var b = this.stripBody;
28768         if(b.getWidth() > w){
28769             var tabs = this.items;
28770             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28771             if(availWidth < this.minTabWidth){
28772                 /*if(!this.sleft){    // incomplete scrolling code
28773                     this.createScrollButtons();
28774                 }
28775                 this.showScroll();
28776                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28777             }
28778         }else{
28779             if(this.currentTabWidth < this.preferredTabWidth){
28780                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28781             }
28782         }
28783     },
28784
28785     /**
28786      * Returns the number of tabs in this TabPanel.
28787      * @return {Number}
28788      */
28789      getCount : function(){
28790          return this.items.length;
28791      },
28792
28793     /**
28794      * Resizes all the tabs to the passed width
28795      * @param {Number} The new width
28796      */
28797     setTabWidth : function(width){
28798         this.currentTabWidth = width;
28799         for(var i = 0, len = this.items.length; i < len; i++) {
28800                 if(!this.items[i].isHidden()) {
28801                 this.items[i].setWidth(width);
28802             }
28803         }
28804     },
28805
28806     /**
28807      * Destroys this TabPanel
28808      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28809      */
28810     destroy : function(removeEl){
28811         Roo.EventManager.removeResizeListener(this.onResize, this);
28812         for(var i = 0, len = this.items.length; i < len; i++){
28813             this.items[i].purgeListeners();
28814         }
28815         if(removeEl === true){
28816             this.el.update("");
28817             this.el.remove();
28818         }
28819     }
28820 });
28821
28822 /**
28823  * @class Roo.TabPanelItem
28824  * @extends Roo.util.Observable
28825  * Represents an individual item (tab plus body) in a TabPanel.
28826  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28827  * @param {String} id The id of this TabPanelItem
28828  * @param {String} text The text for the tab of this TabPanelItem
28829  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28830  */
28831 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28832     /**
28833      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28834      * @type Roo.TabPanel
28835      */
28836     this.tabPanel = tabPanel;
28837     /**
28838      * The id for this TabPanelItem
28839      * @type String
28840      */
28841     this.id = id;
28842     /** @private */
28843     this.disabled = false;
28844     /** @private */
28845     this.text = text;
28846     /** @private */
28847     this.loaded = false;
28848     this.closable = closable;
28849
28850     /**
28851      * The body element for this TabPanelItem.
28852      * @type Roo.Element
28853      */
28854     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28855     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28856     this.bodyEl.setStyle("display", "block");
28857     this.bodyEl.setStyle("zoom", "1");
28858     this.hideAction();
28859
28860     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28861     /** @private */
28862     this.el = Roo.get(els.el, true);
28863     this.inner = Roo.get(els.inner, true);
28864     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28865     this.pnode = Roo.get(els.el.parentNode, true);
28866     this.el.on("mousedown", this.onTabMouseDown, this);
28867     this.el.on("click", this.onTabClick, this);
28868     /** @private */
28869     if(closable){
28870         var c = Roo.get(els.close, true);
28871         c.dom.title = this.closeText;
28872         c.addClassOnOver("close-over");
28873         c.on("click", this.closeClick, this);
28874      }
28875
28876     this.addEvents({
28877          /**
28878          * @event activate
28879          * Fires when this tab becomes the active tab.
28880          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28881          * @param {Roo.TabPanelItem} this
28882          */
28883         "activate": true,
28884         /**
28885          * @event beforeclose
28886          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28887          * @param {Roo.TabPanelItem} this
28888          * @param {Object} e Set cancel to true on this object to cancel the close.
28889          */
28890         "beforeclose": true,
28891         /**
28892          * @event close
28893          * Fires when this tab is closed.
28894          * @param {Roo.TabPanelItem} this
28895          */
28896          "close": true,
28897         /**
28898          * @event deactivate
28899          * Fires when this tab is no longer the active tab.
28900          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28901          * @param {Roo.TabPanelItem} this
28902          */
28903          "deactivate" : true
28904     });
28905     this.hidden = false;
28906
28907     Roo.TabPanelItem.superclass.constructor.call(this);
28908 };
28909
28910 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28911     purgeListeners : function(){
28912        Roo.util.Observable.prototype.purgeListeners.call(this);
28913        this.el.removeAllListeners();
28914     },
28915     /**
28916      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28917      */
28918     show : function(){
28919         this.pnode.addClass("on");
28920         this.showAction();
28921         if(Roo.isOpera){
28922             this.tabPanel.stripWrap.repaint();
28923         }
28924         this.fireEvent("activate", this.tabPanel, this);
28925     },
28926
28927     /**
28928      * Returns true if this tab is the active tab.
28929      * @return {Boolean}
28930      */
28931     isActive : function(){
28932         return this.tabPanel.getActiveTab() == this;
28933     },
28934
28935     /**
28936      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28937      */
28938     hide : function(){
28939         this.pnode.removeClass("on");
28940         this.hideAction();
28941         this.fireEvent("deactivate", this.tabPanel, this);
28942     },
28943
28944     hideAction : function(){
28945         this.bodyEl.hide();
28946         this.bodyEl.setStyle("position", "absolute");
28947         this.bodyEl.setLeft("-20000px");
28948         this.bodyEl.setTop("-20000px");
28949     },
28950
28951     showAction : function(){
28952         this.bodyEl.setStyle("position", "relative");
28953         this.bodyEl.setTop("");
28954         this.bodyEl.setLeft("");
28955         this.bodyEl.show();
28956     },
28957
28958     /**
28959      * Set the tooltip for the tab.
28960      * @param {String} tooltip The tab's tooltip
28961      */
28962     setTooltip : function(text){
28963         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28964             this.textEl.dom.qtip = text;
28965             this.textEl.dom.removeAttribute('title');
28966         }else{
28967             this.textEl.dom.title = text;
28968         }
28969     },
28970
28971     onTabClick : function(e){
28972         e.preventDefault();
28973         this.tabPanel.activate(this.id);
28974     },
28975
28976     onTabMouseDown : function(e){
28977         e.preventDefault();
28978         this.tabPanel.activate(this.id);
28979     },
28980
28981     getWidth : function(){
28982         return this.inner.getWidth();
28983     },
28984
28985     setWidth : function(width){
28986         var iwidth = width - this.pnode.getPadding("lr");
28987         this.inner.setWidth(iwidth);
28988         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28989         this.pnode.setWidth(width);
28990     },
28991
28992     /**
28993      * Show or hide the tab
28994      * @param {Boolean} hidden True to hide or false to show.
28995      */
28996     setHidden : function(hidden){
28997         this.hidden = hidden;
28998         this.pnode.setStyle("display", hidden ? "none" : "");
28999     },
29000
29001     /**
29002      * Returns true if this tab is "hidden"
29003      * @return {Boolean}
29004      */
29005     isHidden : function(){
29006         return this.hidden;
29007     },
29008
29009     /**
29010      * Returns the text for this tab
29011      * @return {String}
29012      */
29013     getText : function(){
29014         return this.text;
29015     },
29016
29017     autoSize : function(){
29018         //this.el.beginMeasure();
29019         this.textEl.setWidth(1);
29020         /*
29021          *  #2804 [new] Tabs in Roojs
29022          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29023          */
29024         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29025         //this.el.endMeasure();
29026     },
29027
29028     /**
29029      * Sets the text for the tab (Note: this also sets the tooltip text)
29030      * @param {String} text The tab's text and tooltip
29031      */
29032     setText : function(text){
29033         this.text = text;
29034         this.textEl.update(text);
29035         this.setTooltip(text);
29036         if(!this.tabPanel.resizeTabs){
29037             this.autoSize();
29038         }
29039     },
29040     /**
29041      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29042      */
29043     activate : function(){
29044         this.tabPanel.activate(this.id);
29045     },
29046
29047     /**
29048      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29049      */
29050     disable : function(){
29051         if(this.tabPanel.active != this){
29052             this.disabled = true;
29053             this.pnode.addClass("disabled");
29054         }
29055     },
29056
29057     /**
29058      * Enables this TabPanelItem if it was previously disabled.
29059      */
29060     enable : function(){
29061         this.disabled = false;
29062         this.pnode.removeClass("disabled");
29063     },
29064
29065     /**
29066      * Sets the content for this TabPanelItem.
29067      * @param {String} content The content
29068      * @param {Boolean} loadScripts true to look for and load scripts
29069      */
29070     setContent : function(content, loadScripts){
29071         this.bodyEl.update(content, loadScripts);
29072     },
29073
29074     /**
29075      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29076      * @return {Roo.UpdateManager} The UpdateManager
29077      */
29078     getUpdateManager : function(){
29079         return this.bodyEl.getUpdateManager();
29080     },
29081
29082     /**
29083      * Set a URL to be used to load the content for this TabPanelItem.
29084      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29085      * @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)
29086      * @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)
29087      * @return {Roo.UpdateManager} The UpdateManager
29088      */
29089     setUrl : function(url, params, loadOnce){
29090         if(this.refreshDelegate){
29091             this.un('activate', this.refreshDelegate);
29092         }
29093         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29094         this.on("activate", this.refreshDelegate);
29095         return this.bodyEl.getUpdateManager();
29096     },
29097
29098     /** @private */
29099     _handleRefresh : function(url, params, loadOnce){
29100         if(!loadOnce || !this.loaded){
29101             var updater = this.bodyEl.getUpdateManager();
29102             updater.update(url, params, this._setLoaded.createDelegate(this));
29103         }
29104     },
29105
29106     /**
29107      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29108      *   Will fail silently if the setUrl method has not been called.
29109      *   This does not activate the panel, just updates its content.
29110      */
29111     refresh : function(){
29112         if(this.refreshDelegate){
29113            this.loaded = false;
29114            this.refreshDelegate();
29115         }
29116     },
29117
29118     /** @private */
29119     _setLoaded : function(){
29120         this.loaded = true;
29121     },
29122
29123     /** @private */
29124     closeClick : function(e){
29125         var o = {};
29126         e.stopEvent();
29127         this.fireEvent("beforeclose", this, o);
29128         if(o.cancel !== true){
29129             this.tabPanel.removeTab(this.id);
29130         }
29131     },
29132     /**
29133      * The text displayed in the tooltip for the close icon.
29134      * @type String
29135      */
29136     closeText : "Close this tab"
29137 });
29138
29139 /** @private */
29140 Roo.TabPanel.prototype.createStrip = function(container){
29141     var strip = document.createElement("div");
29142     strip.className = "x-tabs-wrap";
29143     container.appendChild(strip);
29144     return strip;
29145 };
29146 /** @private */
29147 Roo.TabPanel.prototype.createStripList = function(strip){
29148     // div wrapper for retard IE
29149     // returns the "tr" element.
29150     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29151         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29152         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29153     return strip.firstChild.firstChild.firstChild.firstChild;
29154 };
29155 /** @private */
29156 Roo.TabPanel.prototype.createBody = function(container){
29157     var body = document.createElement("div");
29158     Roo.id(body, "tab-body");
29159     Roo.fly(body).addClass("x-tabs-body");
29160     container.appendChild(body);
29161     return body;
29162 };
29163 /** @private */
29164 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29165     var body = Roo.getDom(id);
29166     if(!body){
29167         body = document.createElement("div");
29168         body.id = id;
29169     }
29170     Roo.fly(body).addClass("x-tabs-item-body");
29171     bodyEl.insertBefore(body, bodyEl.firstChild);
29172     return body;
29173 };
29174 /** @private */
29175 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29176     var td = document.createElement("td");
29177     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29178     //stripEl.appendChild(td);
29179     if(closable){
29180         td.className = "x-tabs-closable";
29181         if(!this.closeTpl){
29182             this.closeTpl = new Roo.Template(
29183                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29184                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29185                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29186             );
29187         }
29188         var el = this.closeTpl.overwrite(td, {"text": text});
29189         var close = el.getElementsByTagName("div")[0];
29190         var inner = el.getElementsByTagName("em")[0];
29191         return {"el": el, "close": close, "inner": inner};
29192     } else {
29193         if(!this.tabTpl){
29194             this.tabTpl = new Roo.Template(
29195                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29196                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29197             );
29198         }
29199         var el = this.tabTpl.overwrite(td, {"text": text});
29200         var inner = el.getElementsByTagName("em")[0];
29201         return {"el": el, "inner": inner};
29202     }
29203 };/*
29204  * Based on:
29205  * Ext JS Library 1.1.1
29206  * Copyright(c) 2006-2007, Ext JS, LLC.
29207  *
29208  * Originally Released Under LGPL - original licence link has changed is not relivant.
29209  *
29210  * Fork - LGPL
29211  * <script type="text/javascript">
29212  */
29213
29214 /**
29215  * @class Roo.Button
29216  * @extends Roo.util.Observable
29217  * Simple Button class
29218  * @cfg {String} text The button text
29219  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29220  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29221  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29222  * @cfg {Object} scope The scope of the handler
29223  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29224  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29225  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29226  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29227  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29228  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29229    applies if enableToggle = true)
29230  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29231  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29232   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29233  * @constructor
29234  * Create a new button
29235  * @param {Object} config The config object
29236  */
29237 Roo.Button = function(renderTo, config)
29238 {
29239     if (!config) {
29240         config = renderTo;
29241         renderTo = config.renderTo || false;
29242     }
29243     
29244     Roo.apply(this, config);
29245     this.addEvents({
29246         /**
29247              * @event click
29248              * Fires when this button is clicked
29249              * @param {Button} this
29250              * @param {EventObject} e The click event
29251              */
29252             "click" : true,
29253         /**
29254              * @event toggle
29255              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29256              * @param {Button} this
29257              * @param {Boolean} pressed
29258              */
29259             "toggle" : true,
29260         /**
29261              * @event mouseover
29262              * Fires when the mouse hovers over the button
29263              * @param {Button} this
29264              * @param {Event} e The event object
29265              */
29266         'mouseover' : true,
29267         /**
29268              * @event mouseout
29269              * Fires when the mouse exits the button
29270              * @param {Button} this
29271              * @param {Event} e The event object
29272              */
29273         'mouseout': true,
29274          /**
29275              * @event render
29276              * Fires when the button is rendered
29277              * @param {Button} this
29278              */
29279         'render': true
29280     });
29281     if(this.menu){
29282         this.menu = Roo.menu.MenuMgr.get(this.menu);
29283     }
29284     // register listeners first!!  - so render can be captured..
29285     Roo.util.Observable.call(this);
29286     if(renderTo){
29287         this.render(renderTo);
29288     }
29289     
29290   
29291 };
29292
29293 Roo.extend(Roo.Button, Roo.util.Observable, {
29294     /**
29295      * 
29296      */
29297     
29298     /**
29299      * Read-only. True if this button is hidden
29300      * @type Boolean
29301      */
29302     hidden : false,
29303     /**
29304      * Read-only. True if this button is disabled
29305      * @type Boolean
29306      */
29307     disabled : false,
29308     /**
29309      * Read-only. True if this button is pressed (only if enableToggle = true)
29310      * @type Boolean
29311      */
29312     pressed : false,
29313
29314     /**
29315      * @cfg {Number} tabIndex 
29316      * The DOM tabIndex for this button (defaults to undefined)
29317      */
29318     tabIndex : undefined,
29319
29320     /**
29321      * @cfg {Boolean} enableToggle
29322      * True to enable pressed/not pressed toggling (defaults to false)
29323      */
29324     enableToggle: false,
29325     /**
29326      * @cfg {Mixed} menu
29327      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29328      */
29329     menu : undefined,
29330     /**
29331      * @cfg {String} menuAlign
29332      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29333      */
29334     menuAlign : "tl-bl?",
29335
29336     /**
29337      * @cfg {String} iconCls
29338      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29339      */
29340     iconCls : undefined,
29341     /**
29342      * @cfg {String} type
29343      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29344      */
29345     type : 'button',
29346
29347     // private
29348     menuClassTarget: 'tr',
29349
29350     /**
29351      * @cfg {String} clickEvent
29352      * The type of event to map to the button's event handler (defaults to 'click')
29353      */
29354     clickEvent : 'click',
29355
29356     /**
29357      * @cfg {Boolean} handleMouseEvents
29358      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29359      */
29360     handleMouseEvents : true,
29361
29362     /**
29363      * @cfg {String} tooltipType
29364      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29365      */
29366     tooltipType : 'qtip',
29367
29368     /**
29369      * @cfg {String} cls
29370      * A CSS class to apply to the button's main element.
29371      */
29372     
29373     /**
29374      * @cfg {Roo.Template} template (Optional)
29375      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29376      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29377      * require code modifications if required elements (e.g. a button) aren't present.
29378      */
29379
29380     // private
29381     render : function(renderTo){
29382         var btn;
29383         if(this.hideParent){
29384             this.parentEl = Roo.get(renderTo);
29385         }
29386         if(!this.dhconfig){
29387             if(!this.template){
29388                 if(!Roo.Button.buttonTemplate){
29389                     // hideous table template
29390                     Roo.Button.buttonTemplate = new Roo.Template(
29391                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29392                         '<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>',
29393                         "</tr></tbody></table>");
29394                 }
29395                 this.template = Roo.Button.buttonTemplate;
29396             }
29397             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29398             var btnEl = btn.child("button:first");
29399             btnEl.on('focus', this.onFocus, this);
29400             btnEl.on('blur', this.onBlur, this);
29401             if(this.cls){
29402                 btn.addClass(this.cls);
29403             }
29404             if(this.icon){
29405                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29406             }
29407             if(this.iconCls){
29408                 btnEl.addClass(this.iconCls);
29409                 if(!this.cls){
29410                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29411                 }
29412             }
29413             if(this.tabIndex !== undefined){
29414                 btnEl.dom.tabIndex = this.tabIndex;
29415             }
29416             if(this.tooltip){
29417                 if(typeof this.tooltip == 'object'){
29418                     Roo.QuickTips.tips(Roo.apply({
29419                           target: btnEl.id
29420                     }, this.tooltip));
29421                 } else {
29422                     btnEl.dom[this.tooltipType] = this.tooltip;
29423                 }
29424             }
29425         }else{
29426             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29427         }
29428         this.el = btn;
29429         if(this.id){
29430             this.el.dom.id = this.el.id = this.id;
29431         }
29432         if(this.menu){
29433             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29434             this.menu.on("show", this.onMenuShow, this);
29435             this.menu.on("hide", this.onMenuHide, this);
29436         }
29437         btn.addClass("x-btn");
29438         if(Roo.isIE && !Roo.isIE7){
29439             this.autoWidth.defer(1, this);
29440         }else{
29441             this.autoWidth();
29442         }
29443         if(this.handleMouseEvents){
29444             btn.on("mouseover", this.onMouseOver, this);
29445             btn.on("mouseout", this.onMouseOut, this);
29446             btn.on("mousedown", this.onMouseDown, this);
29447         }
29448         btn.on(this.clickEvent, this.onClick, this);
29449         //btn.on("mouseup", this.onMouseUp, this);
29450         if(this.hidden){
29451             this.hide();
29452         }
29453         if(this.disabled){
29454             this.disable();
29455         }
29456         Roo.ButtonToggleMgr.register(this);
29457         if(this.pressed){
29458             this.el.addClass("x-btn-pressed");
29459         }
29460         if(this.repeat){
29461             var repeater = new Roo.util.ClickRepeater(btn,
29462                 typeof this.repeat == "object" ? this.repeat : {}
29463             );
29464             repeater.on("click", this.onClick,  this);
29465         }
29466         
29467         this.fireEvent('render', this);
29468         
29469     },
29470     /**
29471      * Returns the button's underlying element
29472      * @return {Roo.Element} The element
29473      */
29474     getEl : function(){
29475         return this.el;  
29476     },
29477     
29478     /**
29479      * Destroys this Button and removes any listeners.
29480      */
29481     destroy : function(){
29482         Roo.ButtonToggleMgr.unregister(this);
29483         this.el.removeAllListeners();
29484         this.purgeListeners();
29485         this.el.remove();
29486     },
29487
29488     // private
29489     autoWidth : function(){
29490         if(this.el){
29491             this.el.setWidth("auto");
29492             if(Roo.isIE7 && Roo.isStrict){
29493                 var ib = this.el.child('button');
29494                 if(ib && ib.getWidth() > 20){
29495                     ib.clip();
29496                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29497                 }
29498             }
29499             if(this.minWidth){
29500                 if(this.hidden){
29501                     this.el.beginMeasure();
29502                 }
29503                 if(this.el.getWidth() < this.minWidth){
29504                     this.el.setWidth(this.minWidth);
29505                 }
29506                 if(this.hidden){
29507                     this.el.endMeasure();
29508                 }
29509             }
29510         }
29511     },
29512
29513     /**
29514      * Assigns this button's click handler
29515      * @param {Function} handler The function to call when the button is clicked
29516      * @param {Object} scope (optional) Scope for the function passed in
29517      */
29518     setHandler : function(handler, scope){
29519         this.handler = handler;
29520         this.scope = scope;  
29521     },
29522     
29523     /**
29524      * Sets this button's text
29525      * @param {String} text The button text
29526      */
29527     setText : function(text){
29528         this.text = text;
29529         if(this.el){
29530             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29531         }
29532         this.autoWidth();
29533     },
29534     
29535     /**
29536      * Gets the text for this button
29537      * @return {String} The button text
29538      */
29539     getText : function(){
29540         return this.text;  
29541     },
29542     
29543     /**
29544      * Show this button
29545      */
29546     show: function(){
29547         this.hidden = false;
29548         if(this.el){
29549             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29550         }
29551     },
29552     
29553     /**
29554      * Hide this button
29555      */
29556     hide: function(){
29557         this.hidden = true;
29558         if(this.el){
29559             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29560         }
29561     },
29562     
29563     /**
29564      * Convenience function for boolean show/hide
29565      * @param {Boolean} visible True to show, false to hide
29566      */
29567     setVisible: function(visible){
29568         if(visible) {
29569             this.show();
29570         }else{
29571             this.hide();
29572         }
29573     },
29574     
29575     /**
29576      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29577      * @param {Boolean} state (optional) Force a particular state
29578      */
29579     toggle : function(state){
29580         state = state === undefined ? !this.pressed : state;
29581         if(state != this.pressed){
29582             if(state){
29583                 this.el.addClass("x-btn-pressed");
29584                 this.pressed = true;
29585                 this.fireEvent("toggle", this, true);
29586             }else{
29587                 this.el.removeClass("x-btn-pressed");
29588                 this.pressed = false;
29589                 this.fireEvent("toggle", this, false);
29590             }
29591             if(this.toggleHandler){
29592                 this.toggleHandler.call(this.scope || this, this, state);
29593             }
29594         }
29595     },
29596     
29597     /**
29598      * Focus the button
29599      */
29600     focus : function(){
29601         this.el.child('button:first').focus();
29602     },
29603     
29604     /**
29605      * Disable this button
29606      */
29607     disable : function(){
29608         if(this.el){
29609             this.el.addClass("x-btn-disabled");
29610         }
29611         this.disabled = true;
29612     },
29613     
29614     /**
29615      * Enable this button
29616      */
29617     enable : function(){
29618         if(this.el){
29619             this.el.removeClass("x-btn-disabled");
29620         }
29621         this.disabled = false;
29622     },
29623
29624     /**
29625      * Convenience function for boolean enable/disable
29626      * @param {Boolean} enabled True to enable, false to disable
29627      */
29628     setDisabled : function(v){
29629         this[v !== true ? "enable" : "disable"]();
29630     },
29631
29632     // private
29633     onClick : function(e)
29634     {
29635         if(e){
29636             e.preventDefault();
29637         }
29638         if(e.button != 0){
29639             return;
29640         }
29641         if(!this.disabled){
29642             if(this.enableToggle){
29643                 this.toggle();
29644             }
29645             if(this.menu && !this.menu.isVisible()){
29646                 this.menu.show(this.el, this.menuAlign);
29647             }
29648             this.fireEvent("click", this, e);
29649             if(this.handler){
29650                 this.el.removeClass("x-btn-over");
29651                 this.handler.call(this.scope || this, this, e);
29652             }
29653         }
29654     },
29655     // private
29656     onMouseOver : function(e){
29657         if(!this.disabled){
29658             this.el.addClass("x-btn-over");
29659             this.fireEvent('mouseover', this, e);
29660         }
29661     },
29662     // private
29663     onMouseOut : function(e){
29664         if(!e.within(this.el,  true)){
29665             this.el.removeClass("x-btn-over");
29666             this.fireEvent('mouseout', this, e);
29667         }
29668     },
29669     // private
29670     onFocus : function(e){
29671         if(!this.disabled){
29672             this.el.addClass("x-btn-focus");
29673         }
29674     },
29675     // private
29676     onBlur : function(e){
29677         this.el.removeClass("x-btn-focus");
29678     },
29679     // private
29680     onMouseDown : function(e){
29681         if(!this.disabled && e.button == 0){
29682             this.el.addClass("x-btn-click");
29683             Roo.get(document).on('mouseup', this.onMouseUp, this);
29684         }
29685     },
29686     // private
29687     onMouseUp : function(e){
29688         if(e.button == 0){
29689             this.el.removeClass("x-btn-click");
29690             Roo.get(document).un('mouseup', this.onMouseUp, this);
29691         }
29692     },
29693     // private
29694     onMenuShow : function(e){
29695         this.el.addClass("x-btn-menu-active");
29696     },
29697     // private
29698     onMenuHide : function(e){
29699         this.el.removeClass("x-btn-menu-active");
29700     }   
29701 });
29702
29703 // Private utility class used by Button
29704 Roo.ButtonToggleMgr = function(){
29705    var groups = {};
29706    
29707    function toggleGroup(btn, state){
29708        if(state){
29709            var g = groups[btn.toggleGroup];
29710            for(var i = 0, l = g.length; i < l; i++){
29711                if(g[i] != btn){
29712                    g[i].toggle(false);
29713                }
29714            }
29715        }
29716    }
29717    
29718    return {
29719        register : function(btn){
29720            if(!btn.toggleGroup){
29721                return;
29722            }
29723            var g = groups[btn.toggleGroup];
29724            if(!g){
29725                g = groups[btn.toggleGroup] = [];
29726            }
29727            g.push(btn);
29728            btn.on("toggle", toggleGroup);
29729        },
29730        
29731        unregister : function(btn){
29732            if(!btn.toggleGroup){
29733                return;
29734            }
29735            var g = groups[btn.toggleGroup];
29736            if(g){
29737                g.remove(btn);
29738                btn.un("toggle", toggleGroup);
29739            }
29740        }
29741    };
29742 }();/*
29743  * Based on:
29744  * Ext JS Library 1.1.1
29745  * Copyright(c) 2006-2007, Ext JS, LLC.
29746  *
29747  * Originally Released Under LGPL - original licence link has changed is not relivant.
29748  *
29749  * Fork - LGPL
29750  * <script type="text/javascript">
29751  */
29752  
29753 /**
29754  * @class Roo.SplitButton
29755  * @extends Roo.Button
29756  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29757  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29758  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29759  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29760  * @cfg {String} arrowTooltip The title attribute of the arrow
29761  * @constructor
29762  * Create a new menu button
29763  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29764  * @param {Object} config The config object
29765  */
29766 Roo.SplitButton = function(renderTo, config){
29767     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29768     /**
29769      * @event arrowclick
29770      * Fires when this button's arrow is clicked
29771      * @param {SplitButton} this
29772      * @param {EventObject} e The click event
29773      */
29774     this.addEvents({"arrowclick":true});
29775 };
29776
29777 Roo.extend(Roo.SplitButton, Roo.Button, {
29778     render : function(renderTo){
29779         // this is one sweet looking template!
29780         var tpl = new Roo.Template(
29781             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29782             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29783             '<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>',
29784             "</tbody></table></td><td>",
29785             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29786             '<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>',
29787             "</tbody></table></td></tr></table>"
29788         );
29789         var btn = tpl.append(renderTo, [this.text, this.type], true);
29790         var btnEl = btn.child("button");
29791         if(this.cls){
29792             btn.addClass(this.cls);
29793         }
29794         if(this.icon){
29795             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29796         }
29797         if(this.iconCls){
29798             btnEl.addClass(this.iconCls);
29799             if(!this.cls){
29800                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29801             }
29802         }
29803         this.el = btn;
29804         if(this.handleMouseEvents){
29805             btn.on("mouseover", this.onMouseOver, this);
29806             btn.on("mouseout", this.onMouseOut, this);
29807             btn.on("mousedown", this.onMouseDown, this);
29808             btn.on("mouseup", this.onMouseUp, this);
29809         }
29810         btn.on(this.clickEvent, this.onClick, this);
29811         if(this.tooltip){
29812             if(typeof this.tooltip == 'object'){
29813                 Roo.QuickTips.tips(Roo.apply({
29814                       target: btnEl.id
29815                 }, this.tooltip));
29816             } else {
29817                 btnEl.dom[this.tooltipType] = this.tooltip;
29818             }
29819         }
29820         if(this.arrowTooltip){
29821             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29822         }
29823         if(this.hidden){
29824             this.hide();
29825         }
29826         if(this.disabled){
29827             this.disable();
29828         }
29829         if(this.pressed){
29830             this.el.addClass("x-btn-pressed");
29831         }
29832         if(Roo.isIE && !Roo.isIE7){
29833             this.autoWidth.defer(1, this);
29834         }else{
29835             this.autoWidth();
29836         }
29837         if(this.menu){
29838             this.menu.on("show", this.onMenuShow, this);
29839             this.menu.on("hide", this.onMenuHide, this);
29840         }
29841         this.fireEvent('render', this);
29842     },
29843
29844     // private
29845     autoWidth : function(){
29846         if(this.el){
29847             var tbl = this.el.child("table:first");
29848             var tbl2 = this.el.child("table:last");
29849             this.el.setWidth("auto");
29850             tbl.setWidth("auto");
29851             if(Roo.isIE7 && Roo.isStrict){
29852                 var ib = this.el.child('button:first');
29853                 if(ib && ib.getWidth() > 20){
29854                     ib.clip();
29855                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29856                 }
29857             }
29858             if(this.minWidth){
29859                 if(this.hidden){
29860                     this.el.beginMeasure();
29861                 }
29862                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29863                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29864                 }
29865                 if(this.hidden){
29866                     this.el.endMeasure();
29867                 }
29868             }
29869             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29870         } 
29871     },
29872     /**
29873      * Sets this button's click handler
29874      * @param {Function} handler The function to call when the button is clicked
29875      * @param {Object} scope (optional) Scope for the function passed above
29876      */
29877     setHandler : function(handler, scope){
29878         this.handler = handler;
29879         this.scope = scope;  
29880     },
29881     
29882     /**
29883      * Sets this button's arrow click handler
29884      * @param {Function} handler The function to call when the arrow is clicked
29885      * @param {Object} scope (optional) Scope for the function passed above
29886      */
29887     setArrowHandler : function(handler, scope){
29888         this.arrowHandler = handler;
29889         this.scope = scope;  
29890     },
29891     
29892     /**
29893      * Focus the button
29894      */
29895     focus : function(){
29896         if(this.el){
29897             this.el.child("button:first").focus();
29898         }
29899     },
29900
29901     // private
29902     onClick : function(e){
29903         e.preventDefault();
29904         if(!this.disabled){
29905             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29906                 if(this.menu && !this.menu.isVisible()){
29907                     this.menu.show(this.el, this.menuAlign);
29908                 }
29909                 this.fireEvent("arrowclick", this, e);
29910                 if(this.arrowHandler){
29911                     this.arrowHandler.call(this.scope || this, this, e);
29912                 }
29913             }else{
29914                 this.fireEvent("click", this, e);
29915                 if(this.handler){
29916                     this.handler.call(this.scope || this, this, e);
29917                 }
29918             }
29919         }
29920     },
29921     // private
29922     onMouseDown : function(e){
29923         if(!this.disabled){
29924             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29925         }
29926     },
29927     // private
29928     onMouseUp : function(e){
29929         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29930     }   
29931 });
29932
29933
29934 // backwards compat
29935 Roo.MenuButton = Roo.SplitButton;/*
29936  * Based on:
29937  * Ext JS Library 1.1.1
29938  * Copyright(c) 2006-2007, Ext JS, LLC.
29939  *
29940  * Originally Released Under LGPL - original licence link has changed is not relivant.
29941  *
29942  * Fork - LGPL
29943  * <script type="text/javascript">
29944  */
29945
29946 /**
29947  * @class Roo.Toolbar
29948  * Basic Toolbar class.
29949  * @constructor
29950  * Creates a new Toolbar
29951  * @param {Object} container The config object
29952  */ 
29953 Roo.Toolbar = function(container, buttons, config)
29954 {
29955     /// old consturctor format still supported..
29956     if(container instanceof Array){ // omit the container for later rendering
29957         buttons = container;
29958         config = buttons;
29959         container = null;
29960     }
29961     if (typeof(container) == 'object' && container.xtype) {
29962         config = container;
29963         container = config.container;
29964         buttons = config.buttons || []; // not really - use items!!
29965     }
29966     var xitems = [];
29967     if (config && config.items) {
29968         xitems = config.items;
29969         delete config.items;
29970     }
29971     Roo.apply(this, config);
29972     this.buttons = buttons;
29973     
29974     if(container){
29975         this.render(container);
29976     }
29977     this.xitems = xitems;
29978     Roo.each(xitems, function(b) {
29979         this.add(b);
29980     }, this);
29981     
29982 };
29983
29984 Roo.Toolbar.prototype = {
29985     /**
29986      * @cfg {Array} items
29987      * array of button configs or elements to add (will be converted to a MixedCollection)
29988      */
29989     
29990     /**
29991      * @cfg {String/HTMLElement/Element} container
29992      * The id or element that will contain the toolbar
29993      */
29994     // private
29995     render : function(ct){
29996         this.el = Roo.get(ct);
29997         if(this.cls){
29998             this.el.addClass(this.cls);
29999         }
30000         // using a table allows for vertical alignment
30001         // 100% width is needed by Safari...
30002         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30003         this.tr = this.el.child("tr", true);
30004         var autoId = 0;
30005         this.items = new Roo.util.MixedCollection(false, function(o){
30006             return o.id || ("item" + (++autoId));
30007         });
30008         if(this.buttons){
30009             this.add.apply(this, this.buttons);
30010             delete this.buttons;
30011         }
30012     },
30013
30014     /**
30015      * Adds element(s) to the toolbar -- this function takes a variable number of 
30016      * arguments of mixed type and adds them to the toolbar.
30017      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30018      * <ul>
30019      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30020      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30021      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30022      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30023      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30024      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30025      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30026      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30027      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30028      * </ul>
30029      * @param {Mixed} arg2
30030      * @param {Mixed} etc.
30031      */
30032     add : function(){
30033         var a = arguments, l = a.length;
30034         for(var i = 0; i < l; i++){
30035             this._add(a[i]);
30036         }
30037     },
30038     // private..
30039     _add : function(el) {
30040         
30041         if (el.xtype) {
30042             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30043         }
30044         
30045         if (el.applyTo){ // some kind of form field
30046             return this.addField(el);
30047         } 
30048         if (el.render){ // some kind of Toolbar.Item
30049             return this.addItem(el);
30050         }
30051         if (typeof el == "string"){ // string
30052             if(el == "separator" || el == "-"){
30053                 return this.addSeparator();
30054             }
30055             if (el == " "){
30056                 return this.addSpacer();
30057             }
30058             if(el == "->"){
30059                 return this.addFill();
30060             }
30061             return this.addText(el);
30062             
30063         }
30064         if(el.tagName){ // element
30065             return this.addElement(el);
30066         }
30067         if(typeof el == "object"){ // must be button config?
30068             return this.addButton(el);
30069         }
30070         // and now what?!?!
30071         return false;
30072         
30073     },
30074     
30075     /**
30076      * Add an Xtype element
30077      * @param {Object} xtype Xtype Object
30078      * @return {Object} created Object
30079      */
30080     addxtype : function(e){
30081         return this.add(e);  
30082     },
30083     
30084     /**
30085      * Returns the Element for this toolbar.
30086      * @return {Roo.Element}
30087      */
30088     getEl : function(){
30089         return this.el;  
30090     },
30091     
30092     /**
30093      * Adds a separator
30094      * @return {Roo.Toolbar.Item} The separator item
30095      */
30096     addSeparator : function(){
30097         return this.addItem(new Roo.Toolbar.Separator());
30098     },
30099
30100     /**
30101      * Adds a spacer element
30102      * @return {Roo.Toolbar.Spacer} The spacer item
30103      */
30104     addSpacer : function(){
30105         return this.addItem(new Roo.Toolbar.Spacer());
30106     },
30107
30108     /**
30109      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30110      * @return {Roo.Toolbar.Fill} The fill item
30111      */
30112     addFill : function(){
30113         return this.addItem(new Roo.Toolbar.Fill());
30114     },
30115
30116     /**
30117      * Adds any standard HTML element to the toolbar
30118      * @param {String/HTMLElement/Element} el The element or id of the element to add
30119      * @return {Roo.Toolbar.Item} The element's item
30120      */
30121     addElement : function(el){
30122         return this.addItem(new Roo.Toolbar.Item(el));
30123     },
30124     /**
30125      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30126      * @type Roo.util.MixedCollection  
30127      */
30128     items : false,
30129      
30130     /**
30131      * Adds any Toolbar.Item or subclass
30132      * @param {Roo.Toolbar.Item} item
30133      * @return {Roo.Toolbar.Item} The item
30134      */
30135     addItem : function(item){
30136         var td = this.nextBlock();
30137         item.render(td);
30138         this.items.add(item);
30139         return item;
30140     },
30141     
30142     /**
30143      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30144      * @param {Object/Array} config A button config or array of configs
30145      * @return {Roo.Toolbar.Button/Array}
30146      */
30147     addButton : function(config){
30148         if(config instanceof Array){
30149             var buttons = [];
30150             for(var i = 0, len = config.length; i < len; i++) {
30151                 buttons.push(this.addButton(config[i]));
30152             }
30153             return buttons;
30154         }
30155         var b = config;
30156         if(!(config instanceof Roo.Toolbar.Button)){
30157             b = config.split ?
30158                 new Roo.Toolbar.SplitButton(config) :
30159                 new Roo.Toolbar.Button(config);
30160         }
30161         var td = this.nextBlock();
30162         b.render(td);
30163         this.items.add(b);
30164         return b;
30165     },
30166     
30167     /**
30168      * Adds text to the toolbar
30169      * @param {String} text The text to add
30170      * @return {Roo.Toolbar.Item} The element's item
30171      */
30172     addText : function(text){
30173         return this.addItem(new Roo.Toolbar.TextItem(text));
30174     },
30175     
30176     /**
30177      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30178      * @param {Number} index The index where the item is to be inserted
30179      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30180      * @return {Roo.Toolbar.Button/Item}
30181      */
30182     insertButton : function(index, item){
30183         if(item instanceof Array){
30184             var buttons = [];
30185             for(var i = 0, len = item.length; i < len; i++) {
30186                buttons.push(this.insertButton(index + i, item[i]));
30187             }
30188             return buttons;
30189         }
30190         if (!(item instanceof Roo.Toolbar.Button)){
30191            item = new Roo.Toolbar.Button(item);
30192         }
30193         var td = document.createElement("td");
30194         this.tr.insertBefore(td, this.tr.childNodes[index]);
30195         item.render(td);
30196         this.items.insert(index, item);
30197         return item;
30198     },
30199     
30200     /**
30201      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30202      * @param {Object} config
30203      * @return {Roo.Toolbar.Item} The element's item
30204      */
30205     addDom : function(config, returnEl){
30206         var td = this.nextBlock();
30207         Roo.DomHelper.overwrite(td, config);
30208         var ti = new Roo.Toolbar.Item(td.firstChild);
30209         ti.render(td);
30210         this.items.add(ti);
30211         return ti;
30212     },
30213
30214     /**
30215      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30216      * @type Roo.util.MixedCollection  
30217      */
30218     fields : false,
30219     
30220     /**
30221      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30222      * Note: the field should not have been rendered yet. For a field that has already been
30223      * rendered, use {@link #addElement}.
30224      * @param {Roo.form.Field} field
30225      * @return {Roo.ToolbarItem}
30226      */
30227      
30228       
30229     addField : function(field) {
30230         if (!this.fields) {
30231             var autoId = 0;
30232             this.fields = new Roo.util.MixedCollection(false, function(o){
30233                 return o.id || ("item" + (++autoId));
30234             });
30235
30236         }
30237         
30238         var td = this.nextBlock();
30239         field.render(td);
30240         var ti = new Roo.Toolbar.Item(td.firstChild);
30241         ti.render(td);
30242         this.items.add(ti);
30243         this.fields.add(field);
30244         return ti;
30245     },
30246     /**
30247      * Hide the toolbar
30248      * @method hide
30249      */
30250      
30251       
30252     hide : function()
30253     {
30254         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30255         this.el.child('div').hide();
30256     },
30257     /**
30258      * Show the toolbar
30259      * @method show
30260      */
30261     show : function()
30262     {
30263         this.el.child('div').show();
30264     },
30265       
30266     // private
30267     nextBlock : function(){
30268         var td = document.createElement("td");
30269         this.tr.appendChild(td);
30270         return td;
30271     },
30272
30273     // private
30274     destroy : function(){
30275         if(this.items){ // rendered?
30276             Roo.destroy.apply(Roo, this.items.items);
30277         }
30278         if(this.fields){ // rendered?
30279             Roo.destroy.apply(Roo, this.fields.items);
30280         }
30281         Roo.Element.uncache(this.el, this.tr);
30282     }
30283 };
30284
30285 /**
30286  * @class Roo.Toolbar.Item
30287  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30288  * @constructor
30289  * Creates a new Item
30290  * @param {HTMLElement} el 
30291  */
30292 Roo.Toolbar.Item = function(el){
30293     var cfg = {};
30294     if (typeof (el.xtype) != 'undefined') {
30295         cfg = el;
30296         el = cfg.el;
30297     }
30298     
30299     this.el = Roo.getDom(el);
30300     this.id = Roo.id(this.el);
30301     this.hidden = false;
30302     
30303     this.addEvents({
30304          /**
30305              * @event render
30306              * Fires when the button is rendered
30307              * @param {Button} this
30308              */
30309         'render': true
30310     });
30311     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30312 };
30313 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30314 //Roo.Toolbar.Item.prototype = {
30315     
30316     /**
30317      * Get this item's HTML Element
30318      * @return {HTMLElement}
30319      */
30320     getEl : function(){
30321        return this.el;  
30322     },
30323
30324     // private
30325     render : function(td){
30326         
30327          this.td = td;
30328         td.appendChild(this.el);
30329         
30330         this.fireEvent('render', this);
30331     },
30332     
30333     /**
30334      * Removes and destroys this item.
30335      */
30336     destroy : function(){
30337         this.td.parentNode.removeChild(this.td);
30338     },
30339     
30340     /**
30341      * Shows this item.
30342      */
30343     show: function(){
30344         this.hidden = false;
30345         this.td.style.display = "";
30346     },
30347     
30348     /**
30349      * Hides this item.
30350      */
30351     hide: function(){
30352         this.hidden = true;
30353         this.td.style.display = "none";
30354     },
30355     
30356     /**
30357      * Convenience function for boolean show/hide.
30358      * @param {Boolean} visible true to show/false to hide
30359      */
30360     setVisible: function(visible){
30361         if(visible) {
30362             this.show();
30363         }else{
30364             this.hide();
30365         }
30366     },
30367     
30368     /**
30369      * Try to focus this item.
30370      */
30371     focus : function(){
30372         Roo.fly(this.el).focus();
30373     },
30374     
30375     /**
30376      * Disables this item.
30377      */
30378     disable : function(){
30379         Roo.fly(this.td).addClass("x-item-disabled");
30380         this.disabled = true;
30381         this.el.disabled = true;
30382     },
30383     
30384     /**
30385      * Enables this item.
30386      */
30387     enable : function(){
30388         Roo.fly(this.td).removeClass("x-item-disabled");
30389         this.disabled = false;
30390         this.el.disabled = false;
30391     }
30392 });
30393
30394
30395 /**
30396  * @class Roo.Toolbar.Separator
30397  * @extends Roo.Toolbar.Item
30398  * A simple toolbar separator class
30399  * @constructor
30400  * Creates a new Separator
30401  */
30402 Roo.Toolbar.Separator = function(cfg){
30403     
30404     var s = document.createElement("span");
30405     s.className = "ytb-sep";
30406     if (cfg) {
30407         cfg.el = s;
30408     }
30409     
30410     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30411 };
30412 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30413     enable:Roo.emptyFn,
30414     disable:Roo.emptyFn,
30415     focus:Roo.emptyFn
30416 });
30417
30418 /**
30419  * @class Roo.Toolbar.Spacer
30420  * @extends Roo.Toolbar.Item
30421  * A simple element that adds extra horizontal space to a toolbar.
30422  * @constructor
30423  * Creates a new Spacer
30424  */
30425 Roo.Toolbar.Spacer = function(cfg){
30426     var s = document.createElement("div");
30427     s.className = "ytb-spacer";
30428     if (cfg) {
30429         cfg.el = s;
30430     }
30431     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30432 };
30433 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30434     enable:Roo.emptyFn,
30435     disable:Roo.emptyFn,
30436     focus:Roo.emptyFn
30437 });
30438
30439 /**
30440  * @class Roo.Toolbar.Fill
30441  * @extends Roo.Toolbar.Spacer
30442  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30443  * @constructor
30444  * Creates a new Spacer
30445  */
30446 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30447     // private
30448     render : function(td){
30449         td.style.width = '100%';
30450         Roo.Toolbar.Fill.superclass.render.call(this, td);
30451     }
30452 });
30453
30454 /**
30455  * @class Roo.Toolbar.TextItem
30456  * @extends Roo.Toolbar.Item
30457  * A simple class that renders text directly into a toolbar.
30458  * @constructor
30459  * Creates a new TextItem
30460  * @cfg {string} text 
30461  */
30462 Roo.Toolbar.TextItem = function(cfg){
30463     var  text = cfg || "";
30464     if (typeof(cfg) == 'object') {
30465         text = cfg.text || "";
30466     }  else {
30467         cfg = null;
30468     }
30469     var s = document.createElement("span");
30470     s.className = "ytb-text";
30471     s.innerHTML = text;
30472     if (cfg) {
30473         cfg.el  = s;
30474     }
30475     
30476     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30477 };
30478 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30479     
30480      
30481     enable:Roo.emptyFn,
30482     disable:Roo.emptyFn,
30483     focus:Roo.emptyFn
30484 });
30485
30486 /**
30487  * @class Roo.Toolbar.Button
30488  * @extends Roo.Button
30489  * A button that renders into a toolbar.
30490  * @constructor
30491  * Creates a new Button
30492  * @param {Object} config A standard {@link Roo.Button} config object
30493  */
30494 Roo.Toolbar.Button = function(config){
30495     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30496 };
30497 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30498     render : function(td){
30499         this.td = td;
30500         Roo.Toolbar.Button.superclass.render.call(this, td);
30501     },
30502     
30503     /**
30504      * Removes and destroys this button
30505      */
30506     destroy : function(){
30507         Roo.Toolbar.Button.superclass.destroy.call(this);
30508         this.td.parentNode.removeChild(this.td);
30509     },
30510     
30511     /**
30512      * Shows this button
30513      */
30514     show: function(){
30515         this.hidden = false;
30516         this.td.style.display = "";
30517     },
30518     
30519     /**
30520      * Hides this button
30521      */
30522     hide: function(){
30523         this.hidden = true;
30524         this.td.style.display = "none";
30525     },
30526
30527     /**
30528      * Disables this item
30529      */
30530     disable : function(){
30531         Roo.fly(this.td).addClass("x-item-disabled");
30532         this.disabled = true;
30533     },
30534
30535     /**
30536      * Enables this item
30537      */
30538     enable : function(){
30539         Roo.fly(this.td).removeClass("x-item-disabled");
30540         this.disabled = false;
30541     }
30542 });
30543 // backwards compat
30544 Roo.ToolbarButton = Roo.Toolbar.Button;
30545
30546 /**
30547  * @class Roo.Toolbar.SplitButton
30548  * @extends Roo.SplitButton
30549  * A menu button that renders into a toolbar.
30550  * @constructor
30551  * Creates a new SplitButton
30552  * @param {Object} config A standard {@link Roo.SplitButton} config object
30553  */
30554 Roo.Toolbar.SplitButton = function(config){
30555     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30556 };
30557 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30558     render : function(td){
30559         this.td = td;
30560         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30561     },
30562     
30563     /**
30564      * Removes and destroys this button
30565      */
30566     destroy : function(){
30567         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30568         this.td.parentNode.removeChild(this.td);
30569     },
30570     
30571     /**
30572      * Shows this button
30573      */
30574     show: function(){
30575         this.hidden = false;
30576         this.td.style.display = "";
30577     },
30578     
30579     /**
30580      * Hides this button
30581      */
30582     hide: function(){
30583         this.hidden = true;
30584         this.td.style.display = "none";
30585     }
30586 });
30587
30588 // backwards compat
30589 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30590  * Based on:
30591  * Ext JS Library 1.1.1
30592  * Copyright(c) 2006-2007, Ext JS, LLC.
30593  *
30594  * Originally Released Under LGPL - original licence link has changed is not relivant.
30595  *
30596  * Fork - LGPL
30597  * <script type="text/javascript">
30598  */
30599  
30600 /**
30601  * @class Roo.PagingToolbar
30602  * @extends Roo.Toolbar
30603  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30604  * @constructor
30605  * Create a new PagingToolbar
30606  * @param {Object} config The config object
30607  */
30608 Roo.PagingToolbar = function(el, ds, config)
30609 {
30610     // old args format still supported... - xtype is prefered..
30611     if (typeof(el) == 'object' && el.xtype) {
30612         // created from xtype...
30613         config = el;
30614         ds = el.dataSource;
30615         el = config.container;
30616     }
30617     var items = [];
30618     if (config.items) {
30619         items = config.items;
30620         config.items = [];
30621     }
30622     
30623     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30624     this.ds = ds;
30625     this.cursor = 0;
30626     this.renderButtons(this.el);
30627     this.bind(ds);
30628     
30629     // supprot items array.
30630    
30631     Roo.each(items, function(e) {
30632         this.add(Roo.factory(e));
30633     },this);
30634     
30635 };
30636
30637 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30638     /**
30639      * @cfg {Roo.data.Store} dataSource
30640      * The underlying data store providing the paged data
30641      */
30642     /**
30643      * @cfg {String/HTMLElement/Element} container
30644      * container The id or element that will contain the toolbar
30645      */
30646     /**
30647      * @cfg {Boolean} displayInfo
30648      * True to display the displayMsg (defaults to false)
30649      */
30650     /**
30651      * @cfg {Number} pageSize
30652      * The number of records to display per page (defaults to 20)
30653      */
30654     pageSize: 20,
30655     /**
30656      * @cfg {String} displayMsg
30657      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30658      */
30659     displayMsg : 'Displaying {0} - {1} of {2}',
30660     /**
30661      * @cfg {String} emptyMsg
30662      * The message to display when no records are found (defaults to "No data to display")
30663      */
30664     emptyMsg : 'No data to display',
30665     /**
30666      * Customizable piece of the default paging text (defaults to "Page")
30667      * @type String
30668      */
30669     beforePageText : "Page",
30670     /**
30671      * Customizable piece of the default paging text (defaults to "of %0")
30672      * @type String
30673      */
30674     afterPageText : "of {0}",
30675     /**
30676      * Customizable piece of the default paging text (defaults to "First Page")
30677      * @type String
30678      */
30679     firstText : "First Page",
30680     /**
30681      * Customizable piece of the default paging text (defaults to "Previous Page")
30682      * @type String
30683      */
30684     prevText : "Previous Page",
30685     /**
30686      * Customizable piece of the default paging text (defaults to "Next Page")
30687      * @type String
30688      */
30689     nextText : "Next Page",
30690     /**
30691      * Customizable piece of the default paging text (defaults to "Last Page")
30692      * @type String
30693      */
30694     lastText : "Last Page",
30695     /**
30696      * Customizable piece of the default paging text (defaults to "Refresh")
30697      * @type String
30698      */
30699     refreshText : "Refresh",
30700
30701     // private
30702     renderButtons : function(el){
30703         Roo.PagingToolbar.superclass.render.call(this, el);
30704         this.first = this.addButton({
30705             tooltip: this.firstText,
30706             cls: "x-btn-icon x-grid-page-first",
30707             disabled: true,
30708             handler: this.onClick.createDelegate(this, ["first"])
30709         });
30710         this.prev = this.addButton({
30711             tooltip: this.prevText,
30712             cls: "x-btn-icon x-grid-page-prev",
30713             disabled: true,
30714             handler: this.onClick.createDelegate(this, ["prev"])
30715         });
30716         //this.addSeparator();
30717         this.add(this.beforePageText);
30718         this.field = Roo.get(this.addDom({
30719            tag: "input",
30720            type: "text",
30721            size: "3",
30722            value: "1",
30723            cls: "x-grid-page-number"
30724         }).el);
30725         this.field.on("keydown", this.onPagingKeydown, this);
30726         this.field.on("focus", function(){this.dom.select();});
30727         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30728         this.field.setHeight(18);
30729         //this.addSeparator();
30730         this.next = this.addButton({
30731             tooltip: this.nextText,
30732             cls: "x-btn-icon x-grid-page-next",
30733             disabled: true,
30734             handler: this.onClick.createDelegate(this, ["next"])
30735         });
30736         this.last = this.addButton({
30737             tooltip: this.lastText,
30738             cls: "x-btn-icon x-grid-page-last",
30739             disabled: true,
30740             handler: this.onClick.createDelegate(this, ["last"])
30741         });
30742         //this.addSeparator();
30743         this.loading = this.addButton({
30744             tooltip: this.refreshText,
30745             cls: "x-btn-icon x-grid-loading",
30746             handler: this.onClick.createDelegate(this, ["refresh"])
30747         });
30748
30749         if(this.displayInfo){
30750             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30751         }
30752     },
30753
30754     // private
30755     updateInfo : function(){
30756         if(this.displayEl){
30757             var count = this.ds.getCount();
30758             var msg = count == 0 ?
30759                 this.emptyMsg :
30760                 String.format(
30761                     this.displayMsg,
30762                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30763                 );
30764             this.displayEl.update(msg);
30765         }
30766     },
30767
30768     // private
30769     onLoad : function(ds, r, o){
30770        this.cursor = o.params ? o.params.start : 0;
30771        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30772
30773        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30774        this.field.dom.value = ap;
30775        this.first.setDisabled(ap == 1);
30776        this.prev.setDisabled(ap == 1);
30777        this.next.setDisabled(ap == ps);
30778        this.last.setDisabled(ap == ps);
30779        this.loading.enable();
30780        this.updateInfo();
30781     },
30782
30783     // private
30784     getPageData : function(){
30785         var total = this.ds.getTotalCount();
30786         return {
30787             total : total,
30788             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30789             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30790         };
30791     },
30792
30793     // private
30794     onLoadError : function(){
30795         this.loading.enable();
30796     },
30797
30798     // private
30799     onPagingKeydown : function(e){
30800         var k = e.getKey();
30801         var d = this.getPageData();
30802         if(k == e.RETURN){
30803             var v = this.field.dom.value, pageNum;
30804             if(!v || isNaN(pageNum = parseInt(v, 10))){
30805                 this.field.dom.value = d.activePage;
30806                 return;
30807             }
30808             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30809             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30810             e.stopEvent();
30811         }
30812         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))
30813         {
30814           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30815           this.field.dom.value = pageNum;
30816           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30817           e.stopEvent();
30818         }
30819         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30820         {
30821           var v = this.field.dom.value, pageNum; 
30822           var increment = (e.shiftKey) ? 10 : 1;
30823           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30824             increment *= -1;
30825           }
30826           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30827             this.field.dom.value = d.activePage;
30828             return;
30829           }
30830           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30831           {
30832             this.field.dom.value = parseInt(v, 10) + increment;
30833             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30834             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30835           }
30836           e.stopEvent();
30837         }
30838     },
30839
30840     // private
30841     beforeLoad : function(){
30842         if(this.loading){
30843             this.loading.disable();
30844         }
30845     },
30846
30847     // private
30848     onClick : function(which){
30849         var ds = this.ds;
30850         switch(which){
30851             case "first":
30852                 ds.load({params:{start: 0, limit: this.pageSize}});
30853             break;
30854             case "prev":
30855                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30856             break;
30857             case "next":
30858                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30859             break;
30860             case "last":
30861                 var total = ds.getTotalCount();
30862                 var extra = total % this.pageSize;
30863                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30864                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30865             break;
30866             case "refresh":
30867                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30868             break;
30869         }
30870     },
30871
30872     /**
30873      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30874      * @param {Roo.data.Store} store The data store to unbind
30875      */
30876     unbind : function(ds){
30877         ds.un("beforeload", this.beforeLoad, this);
30878         ds.un("load", this.onLoad, this);
30879         ds.un("loadexception", this.onLoadError, this);
30880         ds.un("remove", this.updateInfo, this);
30881         ds.un("add", this.updateInfo, this);
30882         this.ds = undefined;
30883     },
30884
30885     /**
30886      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30887      * @param {Roo.data.Store} store The data store to bind
30888      */
30889     bind : function(ds){
30890         ds.on("beforeload", this.beforeLoad, this);
30891         ds.on("load", this.onLoad, this);
30892         ds.on("loadexception", this.onLoadError, this);
30893         ds.on("remove", this.updateInfo, this);
30894         ds.on("add", this.updateInfo, this);
30895         this.ds = ds;
30896     }
30897 });/*
30898  * Based on:
30899  * Ext JS Library 1.1.1
30900  * Copyright(c) 2006-2007, Ext JS, LLC.
30901  *
30902  * Originally Released Under LGPL - original licence link has changed is not relivant.
30903  *
30904  * Fork - LGPL
30905  * <script type="text/javascript">
30906  */
30907
30908 /**
30909  * @class Roo.Resizable
30910  * @extends Roo.util.Observable
30911  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30912  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30913  * 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
30914  * the element will be wrapped for you automatically.</p>
30915  * <p>Here is the list of valid resize handles:</p>
30916  * <pre>
30917 Value   Description
30918 ------  -------------------
30919  'n'     north
30920  's'     south
30921  'e'     east
30922  'w'     west
30923  'nw'    northwest
30924  'sw'    southwest
30925  'se'    southeast
30926  'ne'    northeast
30927  'hd'    horizontal drag
30928  'all'   all
30929 </pre>
30930  * <p>Here's an example showing the creation of a typical Resizable:</p>
30931  * <pre><code>
30932 var resizer = new Roo.Resizable("element-id", {
30933     handles: 'all',
30934     minWidth: 200,
30935     minHeight: 100,
30936     maxWidth: 500,
30937     maxHeight: 400,
30938     pinned: true
30939 });
30940 resizer.on("resize", myHandler);
30941 </code></pre>
30942  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30943  * resizer.east.setDisplayed(false);</p>
30944  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30945  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30946  * resize operation's new size (defaults to [0, 0])
30947  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30948  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30949  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30950  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30951  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30952  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30953  * @cfg {Number} width The width of the element in pixels (defaults to null)
30954  * @cfg {Number} height The height of the element in pixels (defaults to null)
30955  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30956  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30957  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30958  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30959  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30960  * in favor of the handles config option (defaults to false)
30961  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30962  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30963  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30964  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30965  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30966  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30967  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30968  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30969  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30970  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30971  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30972  * @constructor
30973  * Create a new resizable component
30974  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30975  * @param {Object} config configuration options
30976   */
30977 Roo.Resizable = function(el, config)
30978 {
30979     this.el = Roo.get(el);
30980
30981     if(config && config.wrap){
30982         config.resizeChild = this.el;
30983         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30984         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30985         this.el.setStyle("overflow", "hidden");
30986         this.el.setPositioning(config.resizeChild.getPositioning());
30987         config.resizeChild.clearPositioning();
30988         if(!config.width || !config.height){
30989             var csize = config.resizeChild.getSize();
30990             this.el.setSize(csize.width, csize.height);
30991         }
30992         if(config.pinned && !config.adjustments){
30993             config.adjustments = "auto";
30994         }
30995     }
30996
30997     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30998     this.proxy.unselectable();
30999     this.proxy.enableDisplayMode('block');
31000
31001     Roo.apply(this, config);
31002
31003     if(this.pinned){
31004         this.disableTrackOver = true;
31005         this.el.addClass("x-resizable-pinned");
31006     }
31007     // if the element isn't positioned, make it relative
31008     var position = this.el.getStyle("position");
31009     if(position != "absolute" && position != "fixed"){
31010         this.el.setStyle("position", "relative");
31011     }
31012     if(!this.handles){ // no handles passed, must be legacy style
31013         this.handles = 's,e,se';
31014         if(this.multiDirectional){
31015             this.handles += ',n,w';
31016         }
31017     }
31018     if(this.handles == "all"){
31019         this.handles = "n s e w ne nw se sw";
31020     }
31021     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31022     var ps = Roo.Resizable.positions;
31023     for(var i = 0, len = hs.length; i < len; i++){
31024         if(hs[i] && ps[hs[i]]){
31025             var pos = ps[hs[i]];
31026             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31027         }
31028     }
31029     // legacy
31030     this.corner = this.southeast;
31031     
31032     // updateBox = the box can move..
31033     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31034         this.updateBox = true;
31035     }
31036
31037     this.activeHandle = null;
31038
31039     if(this.resizeChild){
31040         if(typeof this.resizeChild == "boolean"){
31041             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31042         }else{
31043             this.resizeChild = Roo.get(this.resizeChild, true);
31044         }
31045     }
31046     
31047     if(this.adjustments == "auto"){
31048         var rc = this.resizeChild;
31049         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31050         if(rc && (hw || hn)){
31051             rc.position("relative");
31052             rc.setLeft(hw ? hw.el.getWidth() : 0);
31053             rc.setTop(hn ? hn.el.getHeight() : 0);
31054         }
31055         this.adjustments = [
31056             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31057             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31058         ];
31059     }
31060
31061     if(this.draggable){
31062         this.dd = this.dynamic ?
31063             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31064         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31065     }
31066
31067     // public events
31068     this.addEvents({
31069         /**
31070          * @event beforeresize
31071          * Fired before resize is allowed. Set enabled to false to cancel resize.
31072          * @param {Roo.Resizable} this
31073          * @param {Roo.EventObject} e The mousedown event
31074          */
31075         "beforeresize" : true,
31076         /**
31077          * @event resizing
31078          * Fired a resizing.
31079          * @param {Roo.Resizable} this
31080          * @param {Number} x The new x position
31081          * @param {Number} y The new y position
31082          * @param {Number} w The new w width
31083          * @param {Number} h The new h hight
31084          * @param {Roo.EventObject} e The mouseup event
31085          */
31086         "resizing" : true,
31087         /**
31088          * @event resize
31089          * Fired after a resize.
31090          * @param {Roo.Resizable} this
31091          * @param {Number} width The new width
31092          * @param {Number} height The new height
31093          * @param {Roo.EventObject} e The mouseup event
31094          */
31095         "resize" : true
31096     });
31097
31098     if(this.width !== null && this.height !== null){
31099         this.resizeTo(this.width, this.height);
31100     }else{
31101         this.updateChildSize();
31102     }
31103     if(Roo.isIE){
31104         this.el.dom.style.zoom = 1;
31105     }
31106     Roo.Resizable.superclass.constructor.call(this);
31107 };
31108
31109 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31110         resizeChild : false,
31111         adjustments : [0, 0],
31112         minWidth : 5,
31113         minHeight : 5,
31114         maxWidth : 10000,
31115         maxHeight : 10000,
31116         enabled : true,
31117         animate : false,
31118         duration : .35,
31119         dynamic : false,
31120         handles : false,
31121         multiDirectional : false,
31122         disableTrackOver : false,
31123         easing : 'easeOutStrong',
31124         widthIncrement : 0,
31125         heightIncrement : 0,
31126         pinned : false,
31127         width : null,
31128         height : null,
31129         preserveRatio : false,
31130         transparent: false,
31131         minX: 0,
31132         minY: 0,
31133         draggable: false,
31134
31135         /**
31136          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31137          */
31138         constrainTo: undefined,
31139         /**
31140          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31141          */
31142         resizeRegion: undefined,
31143
31144
31145     /**
31146      * Perform a manual resize
31147      * @param {Number} width
31148      * @param {Number} height
31149      */
31150     resizeTo : function(width, height){
31151         this.el.setSize(width, height);
31152         this.updateChildSize();
31153         this.fireEvent("resize", this, width, height, null);
31154     },
31155
31156     // private
31157     startSizing : function(e, handle){
31158         this.fireEvent("beforeresize", this, e);
31159         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31160
31161             if(!this.overlay){
31162                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31163                 this.overlay.unselectable();
31164                 this.overlay.enableDisplayMode("block");
31165                 this.overlay.on("mousemove", this.onMouseMove, this);
31166                 this.overlay.on("mouseup", this.onMouseUp, this);
31167             }
31168             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31169
31170             this.resizing = true;
31171             this.startBox = this.el.getBox();
31172             this.startPoint = e.getXY();
31173             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31174                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31175
31176             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31177             this.overlay.show();
31178
31179             if(this.constrainTo) {
31180                 var ct = Roo.get(this.constrainTo);
31181                 this.resizeRegion = ct.getRegion().adjust(
31182                     ct.getFrameWidth('t'),
31183                     ct.getFrameWidth('l'),
31184                     -ct.getFrameWidth('b'),
31185                     -ct.getFrameWidth('r')
31186                 );
31187             }
31188
31189             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31190             this.proxy.show();
31191             this.proxy.setBox(this.startBox);
31192             if(!this.dynamic){
31193                 this.proxy.setStyle('visibility', 'visible');
31194             }
31195         }
31196     },
31197
31198     // private
31199     onMouseDown : function(handle, e){
31200         if(this.enabled){
31201             e.stopEvent();
31202             this.activeHandle = handle;
31203             this.startSizing(e, handle);
31204         }
31205     },
31206
31207     // private
31208     onMouseUp : function(e){
31209         var size = this.resizeElement();
31210         this.resizing = false;
31211         this.handleOut();
31212         this.overlay.hide();
31213         this.proxy.hide();
31214         this.fireEvent("resize", this, size.width, size.height, e);
31215     },
31216
31217     // private
31218     updateChildSize : function(){
31219         
31220         if(this.resizeChild){
31221             var el = this.el;
31222             var child = this.resizeChild;
31223             var adj = this.adjustments;
31224             if(el.dom.offsetWidth){
31225                 var b = el.getSize(true);
31226                 child.setSize(b.width+adj[0], b.height+adj[1]);
31227             }
31228             // Second call here for IE
31229             // The first call enables instant resizing and
31230             // the second call corrects scroll bars if they
31231             // exist
31232             if(Roo.isIE){
31233                 setTimeout(function(){
31234                     if(el.dom.offsetWidth){
31235                         var b = el.getSize(true);
31236                         child.setSize(b.width+adj[0], b.height+adj[1]);
31237                     }
31238                 }, 10);
31239             }
31240         }
31241     },
31242
31243     // private
31244     snap : function(value, inc, min){
31245         if(!inc || !value) {
31246             return value;
31247         }
31248         var newValue = value;
31249         var m = value % inc;
31250         if(m > 0){
31251             if(m > (inc/2)){
31252                 newValue = value + (inc-m);
31253             }else{
31254                 newValue = value - m;
31255             }
31256         }
31257         return Math.max(min, newValue);
31258     },
31259
31260     // private
31261     resizeElement : function(){
31262         var box = this.proxy.getBox();
31263         if(this.updateBox){
31264             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31265         }else{
31266             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31267         }
31268         this.updateChildSize();
31269         if(!this.dynamic){
31270             this.proxy.hide();
31271         }
31272         return box;
31273     },
31274
31275     // private
31276     constrain : function(v, diff, m, mx){
31277         if(v - diff < m){
31278             diff = v - m;
31279         }else if(v - diff > mx){
31280             diff = mx - v;
31281         }
31282         return diff;
31283     },
31284
31285     // private
31286     onMouseMove : function(e){
31287         
31288         if(this.enabled){
31289             try{// try catch so if something goes wrong the user doesn't get hung
31290
31291             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31292                 return;
31293             }
31294
31295             //var curXY = this.startPoint;
31296             var curSize = this.curSize || this.startBox;
31297             var x = this.startBox.x, y = this.startBox.y;
31298             var ox = x, oy = y;
31299             var w = curSize.width, h = curSize.height;
31300             var ow = w, oh = h;
31301             var mw = this.minWidth, mh = this.minHeight;
31302             var mxw = this.maxWidth, mxh = this.maxHeight;
31303             var wi = this.widthIncrement;
31304             var hi = this.heightIncrement;
31305
31306             var eventXY = e.getXY();
31307             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31308             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31309
31310             var pos = this.activeHandle.position;
31311
31312             switch(pos){
31313                 case "east":
31314                     w += diffX;
31315                     w = Math.min(Math.max(mw, w), mxw);
31316                     break;
31317              
31318                 case "south":
31319                     h += diffY;
31320                     h = Math.min(Math.max(mh, h), mxh);
31321                     break;
31322                 case "southeast":
31323                     w += diffX;
31324                     h += diffY;
31325                     w = Math.min(Math.max(mw, w), mxw);
31326                     h = Math.min(Math.max(mh, h), mxh);
31327                     break;
31328                 case "north":
31329                     diffY = this.constrain(h, diffY, mh, mxh);
31330                     y += diffY;
31331                     h -= diffY;
31332                     break;
31333                 case "hdrag":
31334                     
31335                     if (wi) {
31336                         var adiffX = Math.abs(diffX);
31337                         var sub = (adiffX % wi); // how much 
31338                         if (sub > (wi/2)) { // far enough to snap
31339                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31340                         } else {
31341                             // remove difference.. 
31342                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31343                         }
31344                     }
31345                     x += diffX;
31346                     x = Math.max(this.minX, x);
31347                     break;
31348                 case "west":
31349                     diffX = this.constrain(w, diffX, mw, mxw);
31350                     x += diffX;
31351                     w -= diffX;
31352                     break;
31353                 case "northeast":
31354                     w += diffX;
31355                     w = Math.min(Math.max(mw, w), mxw);
31356                     diffY = this.constrain(h, diffY, mh, mxh);
31357                     y += diffY;
31358                     h -= diffY;
31359                     break;
31360                 case "northwest":
31361                     diffX = this.constrain(w, diffX, mw, mxw);
31362                     diffY = this.constrain(h, diffY, mh, mxh);
31363                     y += diffY;
31364                     h -= diffY;
31365                     x += diffX;
31366                     w -= diffX;
31367                     break;
31368                case "southwest":
31369                     diffX = this.constrain(w, diffX, mw, mxw);
31370                     h += diffY;
31371                     h = Math.min(Math.max(mh, h), mxh);
31372                     x += diffX;
31373                     w -= diffX;
31374                     break;
31375             }
31376
31377             var sw = this.snap(w, wi, mw);
31378             var sh = this.snap(h, hi, mh);
31379             if(sw != w || sh != h){
31380                 switch(pos){
31381                     case "northeast":
31382                         y -= sh - h;
31383                     break;
31384                     case "north":
31385                         y -= sh - h;
31386                         break;
31387                     case "southwest":
31388                         x -= sw - w;
31389                     break;
31390                     case "west":
31391                         x -= sw - w;
31392                         break;
31393                     case "northwest":
31394                         x -= sw - w;
31395                         y -= sh - h;
31396                     break;
31397                 }
31398                 w = sw;
31399                 h = sh;
31400             }
31401
31402             if(this.preserveRatio){
31403                 switch(pos){
31404                     case "southeast":
31405                     case "east":
31406                         h = oh * (w/ow);
31407                         h = Math.min(Math.max(mh, h), mxh);
31408                         w = ow * (h/oh);
31409                        break;
31410                     case "south":
31411                         w = ow * (h/oh);
31412                         w = Math.min(Math.max(mw, w), mxw);
31413                         h = oh * (w/ow);
31414                         break;
31415                     case "northeast":
31416                         w = ow * (h/oh);
31417                         w = Math.min(Math.max(mw, w), mxw);
31418                         h = oh * (w/ow);
31419                     break;
31420                     case "north":
31421                         var tw = w;
31422                         w = ow * (h/oh);
31423                         w = Math.min(Math.max(mw, w), mxw);
31424                         h = oh * (w/ow);
31425                         x += (tw - w) / 2;
31426                         break;
31427                     case "southwest":
31428                         h = oh * (w/ow);
31429                         h = Math.min(Math.max(mh, h), mxh);
31430                         var tw = w;
31431                         w = ow * (h/oh);
31432                         x += tw - w;
31433                         break;
31434                     case "west":
31435                         var th = h;
31436                         h = oh * (w/ow);
31437                         h = Math.min(Math.max(mh, h), mxh);
31438                         y += (th - h) / 2;
31439                         var tw = w;
31440                         w = ow * (h/oh);
31441                         x += tw - w;
31442                        break;
31443                     case "northwest":
31444                         var tw = w;
31445                         var th = h;
31446                         h = oh * (w/ow);
31447                         h = Math.min(Math.max(mh, h), mxh);
31448                         w = ow * (h/oh);
31449                         y += th - h;
31450                         x += tw - w;
31451                        break;
31452
31453                 }
31454             }
31455             if (pos == 'hdrag') {
31456                 w = ow;
31457             }
31458             this.proxy.setBounds(x, y, w, h);
31459             if(this.dynamic){
31460                 this.resizeElement();
31461             }
31462             }catch(e){}
31463         }
31464         this.fireEvent("resizing", this, x, y, w, h, e);
31465     },
31466
31467     // private
31468     handleOver : function(){
31469         if(this.enabled){
31470             this.el.addClass("x-resizable-over");
31471         }
31472     },
31473
31474     // private
31475     handleOut : function(){
31476         if(!this.resizing){
31477             this.el.removeClass("x-resizable-over");
31478         }
31479     },
31480
31481     /**
31482      * Returns the element this component is bound to.
31483      * @return {Roo.Element}
31484      */
31485     getEl : function(){
31486         return this.el;
31487     },
31488
31489     /**
31490      * Returns the resizeChild element (or null).
31491      * @return {Roo.Element}
31492      */
31493     getResizeChild : function(){
31494         return this.resizeChild;
31495     },
31496     groupHandler : function()
31497     {
31498         
31499     },
31500     /**
31501      * Destroys this resizable. If the element was wrapped and
31502      * removeEl is not true then the element remains.
31503      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31504      */
31505     destroy : function(removeEl){
31506         this.proxy.remove();
31507         if(this.overlay){
31508             this.overlay.removeAllListeners();
31509             this.overlay.remove();
31510         }
31511         var ps = Roo.Resizable.positions;
31512         for(var k in ps){
31513             if(typeof ps[k] != "function" && this[ps[k]]){
31514                 var h = this[ps[k]];
31515                 h.el.removeAllListeners();
31516                 h.el.remove();
31517             }
31518         }
31519         if(removeEl){
31520             this.el.update("");
31521             this.el.remove();
31522         }
31523     }
31524 });
31525
31526 // private
31527 // hash to map config positions to true positions
31528 Roo.Resizable.positions = {
31529     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31530     hd: "hdrag"
31531 };
31532
31533 // private
31534 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31535     if(!this.tpl){
31536         // only initialize the template if resizable is used
31537         var tpl = Roo.DomHelper.createTemplate(
31538             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31539         );
31540         tpl.compile();
31541         Roo.Resizable.Handle.prototype.tpl = tpl;
31542     }
31543     this.position = pos;
31544     this.rz = rz;
31545     // show north drag fro topdra
31546     var handlepos = pos == 'hdrag' ? 'north' : pos;
31547     
31548     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31549     if (pos == 'hdrag') {
31550         this.el.setStyle('cursor', 'pointer');
31551     }
31552     this.el.unselectable();
31553     if(transparent){
31554         this.el.setOpacity(0);
31555     }
31556     this.el.on("mousedown", this.onMouseDown, this);
31557     if(!disableTrackOver){
31558         this.el.on("mouseover", this.onMouseOver, this);
31559         this.el.on("mouseout", this.onMouseOut, this);
31560     }
31561 };
31562
31563 // private
31564 Roo.Resizable.Handle.prototype = {
31565     afterResize : function(rz){
31566         Roo.log('after?');
31567         // do nothing
31568     },
31569     // private
31570     onMouseDown : function(e){
31571         this.rz.onMouseDown(this, e);
31572     },
31573     // private
31574     onMouseOver : function(e){
31575         this.rz.handleOver(this, e);
31576     },
31577     // private
31578     onMouseOut : function(e){
31579         this.rz.handleOut(this, e);
31580     }
31581 };/*
31582  * Based on:
31583  * Ext JS Library 1.1.1
31584  * Copyright(c) 2006-2007, Ext JS, LLC.
31585  *
31586  * Originally Released Under LGPL - original licence link has changed is not relivant.
31587  *
31588  * Fork - LGPL
31589  * <script type="text/javascript">
31590  */
31591
31592 /**
31593  * @class Roo.Editor
31594  * @extends Roo.Component
31595  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31596  * @constructor
31597  * Create a new Editor
31598  * @param {Roo.form.Field} field The Field object (or descendant)
31599  * @param {Object} config The config object
31600  */
31601 Roo.Editor = function(field, config){
31602     Roo.Editor.superclass.constructor.call(this, config);
31603     this.field = field;
31604     this.addEvents({
31605         /**
31606              * @event beforestartedit
31607              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31608              * false from the handler of this event.
31609              * @param {Editor} this
31610              * @param {Roo.Element} boundEl The underlying element bound to this editor
31611              * @param {Mixed} value The field value being set
31612              */
31613         "beforestartedit" : true,
31614         /**
31615              * @event startedit
31616              * Fires when this editor is displayed
31617              * @param {Roo.Element} boundEl The underlying element bound to this editor
31618              * @param {Mixed} value The starting field value
31619              */
31620         "startedit" : true,
31621         /**
31622              * @event beforecomplete
31623              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31624              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31625              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31626              * event will not fire since no edit actually occurred.
31627              * @param {Editor} this
31628              * @param {Mixed} value The current field value
31629              * @param {Mixed} startValue The original field value
31630              */
31631         "beforecomplete" : true,
31632         /**
31633              * @event complete
31634              * Fires after editing is complete and any changed value has been written to the underlying field.
31635              * @param {Editor} this
31636              * @param {Mixed} value The current field value
31637              * @param {Mixed} startValue The original field value
31638              */
31639         "complete" : true,
31640         /**
31641          * @event specialkey
31642          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31643          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31644          * @param {Roo.form.Field} this
31645          * @param {Roo.EventObject} e The event object
31646          */
31647         "specialkey" : true
31648     });
31649 };
31650
31651 Roo.extend(Roo.Editor, Roo.Component, {
31652     /**
31653      * @cfg {Boolean/String} autosize
31654      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31655      * or "height" to adopt the height only (defaults to false)
31656      */
31657     /**
31658      * @cfg {Boolean} revertInvalid
31659      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31660      * validation fails (defaults to true)
31661      */
31662     /**
31663      * @cfg {Boolean} ignoreNoChange
31664      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31665      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31666      * will never be ignored.
31667      */
31668     /**
31669      * @cfg {Boolean} hideEl
31670      * False to keep the bound element visible while the editor is displayed (defaults to true)
31671      */
31672     /**
31673      * @cfg {Mixed} value
31674      * The data value of the underlying field (defaults to "")
31675      */
31676     value : "",
31677     /**
31678      * @cfg {String} alignment
31679      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31680      */
31681     alignment: "c-c?",
31682     /**
31683      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31684      * for bottom-right shadow (defaults to "frame")
31685      */
31686     shadow : "frame",
31687     /**
31688      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31689      */
31690     constrain : false,
31691     /**
31692      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31693      */
31694     completeOnEnter : false,
31695     /**
31696      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31697      */
31698     cancelOnEsc : false,
31699     /**
31700      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31701      */
31702     updateEl : false,
31703
31704     // private
31705     onRender : function(ct, position){
31706         this.el = new Roo.Layer({
31707             shadow: this.shadow,
31708             cls: "x-editor",
31709             parentEl : ct,
31710             shim : this.shim,
31711             shadowOffset:4,
31712             id: this.id,
31713             constrain: this.constrain
31714         });
31715         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31716         if(this.field.msgTarget != 'title'){
31717             this.field.msgTarget = 'qtip';
31718         }
31719         this.field.render(this.el);
31720         if(Roo.isGecko){
31721             this.field.el.dom.setAttribute('autocomplete', 'off');
31722         }
31723         this.field.on("specialkey", this.onSpecialKey, this);
31724         if(this.swallowKeys){
31725             this.field.el.swallowEvent(['keydown','keypress']);
31726         }
31727         this.field.show();
31728         this.field.on("blur", this.onBlur, this);
31729         if(this.field.grow){
31730             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31731         }
31732     },
31733
31734     onSpecialKey : function(field, e)
31735     {
31736         //Roo.log('editor onSpecialKey');
31737         if(this.completeOnEnter && e.getKey() == e.ENTER){
31738             e.stopEvent();
31739             this.completeEdit();
31740             return;
31741         }
31742         // do not fire special key otherwise it might hide close the editor...
31743         if(e.getKey() == e.ENTER){    
31744             return;
31745         }
31746         if(this.cancelOnEsc && e.getKey() == e.ESC){
31747             this.cancelEdit();
31748             return;
31749         } 
31750         this.fireEvent('specialkey', field, e);
31751     
31752     },
31753
31754     /**
31755      * Starts the editing process and shows the editor.
31756      * @param {String/HTMLElement/Element} el The element to edit
31757      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31758       * to the innerHTML of el.
31759      */
31760     startEdit : function(el, value){
31761         if(this.editing){
31762             this.completeEdit();
31763         }
31764         this.boundEl = Roo.get(el);
31765         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31766         if(!this.rendered){
31767             this.render(this.parentEl || document.body);
31768         }
31769         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31770             return;
31771         }
31772         this.startValue = v;
31773         this.field.setValue(v);
31774         if(this.autoSize){
31775             var sz = this.boundEl.getSize();
31776             switch(this.autoSize){
31777                 case "width":
31778                 this.setSize(sz.width,  "");
31779                 break;
31780                 case "height":
31781                 this.setSize("",  sz.height);
31782                 break;
31783                 default:
31784                 this.setSize(sz.width,  sz.height);
31785             }
31786         }
31787         this.el.alignTo(this.boundEl, this.alignment);
31788         this.editing = true;
31789         if(Roo.QuickTips){
31790             Roo.QuickTips.disable();
31791         }
31792         this.show();
31793     },
31794
31795     /**
31796      * Sets the height and width of this editor.
31797      * @param {Number} width The new width
31798      * @param {Number} height The new height
31799      */
31800     setSize : function(w, h){
31801         this.field.setSize(w, h);
31802         if(this.el){
31803             this.el.sync();
31804         }
31805     },
31806
31807     /**
31808      * Realigns the editor to the bound field based on the current alignment config value.
31809      */
31810     realign : function(){
31811         this.el.alignTo(this.boundEl, this.alignment);
31812     },
31813
31814     /**
31815      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31816      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31817      */
31818     completeEdit : function(remainVisible){
31819         if(!this.editing){
31820             return;
31821         }
31822         var v = this.getValue();
31823         if(this.revertInvalid !== false && !this.field.isValid()){
31824             v = this.startValue;
31825             this.cancelEdit(true);
31826         }
31827         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31828             this.editing = false;
31829             this.hide();
31830             return;
31831         }
31832         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31833             this.editing = false;
31834             if(this.updateEl && this.boundEl){
31835                 this.boundEl.update(v);
31836             }
31837             if(remainVisible !== true){
31838                 this.hide();
31839             }
31840             this.fireEvent("complete", this, v, this.startValue);
31841         }
31842     },
31843
31844     // private
31845     onShow : function(){
31846         this.el.show();
31847         if(this.hideEl !== false){
31848             this.boundEl.hide();
31849         }
31850         this.field.show();
31851         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31852             this.fixIEFocus = true;
31853             this.deferredFocus.defer(50, this);
31854         }else{
31855             this.field.focus();
31856         }
31857         this.fireEvent("startedit", this.boundEl, this.startValue);
31858     },
31859
31860     deferredFocus : function(){
31861         if(this.editing){
31862             this.field.focus();
31863         }
31864     },
31865
31866     /**
31867      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31868      * reverted to the original starting value.
31869      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31870      * cancel (defaults to false)
31871      */
31872     cancelEdit : function(remainVisible){
31873         if(this.editing){
31874             this.setValue(this.startValue);
31875             if(remainVisible !== true){
31876                 this.hide();
31877             }
31878         }
31879     },
31880
31881     // private
31882     onBlur : function(){
31883         if(this.allowBlur !== true && this.editing){
31884             this.completeEdit();
31885         }
31886     },
31887
31888     // private
31889     onHide : function(){
31890         if(this.editing){
31891             this.completeEdit();
31892             return;
31893         }
31894         this.field.blur();
31895         if(this.field.collapse){
31896             this.field.collapse();
31897         }
31898         this.el.hide();
31899         if(this.hideEl !== false){
31900             this.boundEl.show();
31901         }
31902         if(Roo.QuickTips){
31903             Roo.QuickTips.enable();
31904         }
31905     },
31906
31907     /**
31908      * Sets the data value of the editor
31909      * @param {Mixed} value Any valid value supported by the underlying field
31910      */
31911     setValue : function(v){
31912         this.field.setValue(v);
31913     },
31914
31915     /**
31916      * Gets the data value of the editor
31917      * @return {Mixed} The data value
31918      */
31919     getValue : function(){
31920         return this.field.getValue();
31921     }
31922 });/*
31923  * Based on:
31924  * Ext JS Library 1.1.1
31925  * Copyright(c) 2006-2007, Ext JS, LLC.
31926  *
31927  * Originally Released Under LGPL - original licence link has changed is not relivant.
31928  *
31929  * Fork - LGPL
31930  * <script type="text/javascript">
31931  */
31932  
31933 /**
31934  * @class Roo.BasicDialog
31935  * @extends Roo.util.Observable
31936  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31937  * <pre><code>
31938 var dlg = new Roo.BasicDialog("my-dlg", {
31939     height: 200,
31940     width: 300,
31941     minHeight: 100,
31942     minWidth: 150,
31943     modal: true,
31944     proxyDrag: true,
31945     shadow: true
31946 });
31947 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31948 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31949 dlg.addButton('Cancel', dlg.hide, dlg);
31950 dlg.show();
31951 </code></pre>
31952   <b>A Dialog should always be a direct child of the body element.</b>
31953  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31954  * @cfg {String} title Default text to display in the title bar (defaults to null)
31955  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31956  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31957  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31958  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31959  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31960  * (defaults to null with no animation)
31961  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31962  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31963  * property for valid values (defaults to 'all')
31964  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31965  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31966  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31967  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31968  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31969  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31970  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31971  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31972  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31973  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31974  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31975  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31976  * draggable = true (defaults to false)
31977  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31978  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31979  * shadow (defaults to false)
31980  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31981  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31982  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31983  * @cfg {Array} buttons Array of buttons
31984  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31985  * @constructor
31986  * Create a new BasicDialog.
31987  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31988  * @param {Object} config Configuration options
31989  */
31990 Roo.BasicDialog = function(el, config){
31991     this.el = Roo.get(el);
31992     var dh = Roo.DomHelper;
31993     if(!this.el && config && config.autoCreate){
31994         if(typeof config.autoCreate == "object"){
31995             if(!config.autoCreate.id){
31996                 config.autoCreate.id = el;
31997             }
31998             this.el = dh.append(document.body,
31999                         config.autoCreate, true);
32000         }else{
32001             this.el = dh.append(document.body,
32002                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32003         }
32004     }
32005     el = this.el;
32006     el.setDisplayed(true);
32007     el.hide = this.hideAction;
32008     this.id = el.id;
32009     el.addClass("x-dlg");
32010
32011     Roo.apply(this, config);
32012
32013     this.proxy = el.createProxy("x-dlg-proxy");
32014     this.proxy.hide = this.hideAction;
32015     this.proxy.setOpacity(.5);
32016     this.proxy.hide();
32017
32018     if(config.width){
32019         el.setWidth(config.width);
32020     }
32021     if(config.height){
32022         el.setHeight(config.height);
32023     }
32024     this.size = el.getSize();
32025     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32026         this.xy = [config.x,config.y];
32027     }else{
32028         this.xy = el.getCenterXY(true);
32029     }
32030     /** The header element @type Roo.Element */
32031     this.header = el.child("> .x-dlg-hd");
32032     /** The body element @type Roo.Element */
32033     this.body = el.child("> .x-dlg-bd");
32034     /** The footer element @type Roo.Element */
32035     this.footer = el.child("> .x-dlg-ft");
32036
32037     if(!this.header){
32038         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32039     }
32040     if(!this.body){
32041         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32042     }
32043
32044     this.header.unselectable();
32045     if(this.title){
32046         this.header.update(this.title);
32047     }
32048     // this element allows the dialog to be focused for keyboard event
32049     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32050     this.focusEl.swallowEvent("click", true);
32051
32052     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32053
32054     // wrap the body and footer for special rendering
32055     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32056     if(this.footer){
32057         this.bwrap.dom.appendChild(this.footer.dom);
32058     }
32059
32060     this.bg = this.el.createChild({
32061         tag: "div", cls:"x-dlg-bg",
32062         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32063     });
32064     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32065
32066
32067     if(this.autoScroll !== false && !this.autoTabs){
32068         this.body.setStyle("overflow", "auto");
32069     }
32070
32071     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32072
32073     if(this.closable !== false){
32074         this.el.addClass("x-dlg-closable");
32075         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32076         this.close.on("click", this.closeClick, this);
32077         this.close.addClassOnOver("x-dlg-close-over");
32078     }
32079     if(this.collapsible !== false){
32080         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32081         this.collapseBtn.on("click", this.collapseClick, this);
32082         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32083         this.header.on("dblclick", this.collapseClick, this);
32084     }
32085     if(this.resizable !== false){
32086         this.el.addClass("x-dlg-resizable");
32087         this.resizer = new Roo.Resizable(el, {
32088             minWidth: this.minWidth || 80,
32089             minHeight:this.minHeight || 80,
32090             handles: this.resizeHandles || "all",
32091             pinned: true
32092         });
32093         this.resizer.on("beforeresize", this.beforeResize, this);
32094         this.resizer.on("resize", this.onResize, this);
32095     }
32096     if(this.draggable !== false){
32097         el.addClass("x-dlg-draggable");
32098         if (!this.proxyDrag) {
32099             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32100         }
32101         else {
32102             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32103         }
32104         dd.setHandleElId(this.header.id);
32105         dd.endDrag = this.endMove.createDelegate(this);
32106         dd.startDrag = this.startMove.createDelegate(this);
32107         dd.onDrag = this.onDrag.createDelegate(this);
32108         dd.scroll = false;
32109         this.dd = dd;
32110     }
32111     if(this.modal){
32112         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32113         this.mask.enableDisplayMode("block");
32114         this.mask.hide();
32115         this.el.addClass("x-dlg-modal");
32116     }
32117     if(this.shadow){
32118         this.shadow = new Roo.Shadow({
32119             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32120             offset : this.shadowOffset
32121         });
32122     }else{
32123         this.shadowOffset = 0;
32124     }
32125     if(Roo.useShims && this.shim !== false){
32126         this.shim = this.el.createShim();
32127         this.shim.hide = this.hideAction;
32128         this.shim.hide();
32129     }else{
32130         this.shim = false;
32131     }
32132     if(this.autoTabs){
32133         this.initTabs();
32134     }
32135     if (this.buttons) { 
32136         var bts= this.buttons;
32137         this.buttons = [];
32138         Roo.each(bts, function(b) {
32139             this.addButton(b);
32140         }, this);
32141     }
32142     
32143     
32144     this.addEvents({
32145         /**
32146          * @event keydown
32147          * Fires when a key is pressed
32148          * @param {Roo.BasicDialog} this
32149          * @param {Roo.EventObject} e
32150          */
32151         "keydown" : true,
32152         /**
32153          * @event move
32154          * Fires when this dialog is moved by the user.
32155          * @param {Roo.BasicDialog} this
32156          * @param {Number} x The new page X
32157          * @param {Number} y The new page Y
32158          */
32159         "move" : true,
32160         /**
32161          * @event resize
32162          * Fires when this dialog is resized by the user.
32163          * @param {Roo.BasicDialog} this
32164          * @param {Number} width The new width
32165          * @param {Number} height The new height
32166          */
32167         "resize" : true,
32168         /**
32169          * @event beforehide
32170          * Fires before this dialog is hidden.
32171          * @param {Roo.BasicDialog} this
32172          */
32173         "beforehide" : true,
32174         /**
32175          * @event hide
32176          * Fires when this dialog is hidden.
32177          * @param {Roo.BasicDialog} this
32178          */
32179         "hide" : true,
32180         /**
32181          * @event beforeshow
32182          * Fires before this dialog is shown.
32183          * @param {Roo.BasicDialog} this
32184          */
32185         "beforeshow" : true,
32186         /**
32187          * @event show
32188          * Fires when this dialog is shown.
32189          * @param {Roo.BasicDialog} this
32190          */
32191         "show" : true
32192     });
32193     el.on("keydown", this.onKeyDown, this);
32194     el.on("mousedown", this.toFront, this);
32195     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32196     this.el.hide();
32197     Roo.DialogManager.register(this);
32198     Roo.BasicDialog.superclass.constructor.call(this);
32199 };
32200
32201 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32202     shadowOffset: Roo.isIE ? 6 : 5,
32203     minHeight: 80,
32204     minWidth: 200,
32205     minButtonWidth: 75,
32206     defaultButton: null,
32207     buttonAlign: "right",
32208     tabTag: 'div',
32209     firstShow: true,
32210
32211     /**
32212      * Sets the dialog title text
32213      * @param {String} text The title text to display
32214      * @return {Roo.BasicDialog} this
32215      */
32216     setTitle : function(text){
32217         this.header.update(text);
32218         return this;
32219     },
32220
32221     // private
32222     closeClick : function(){
32223         this.hide();
32224     },
32225
32226     // private
32227     collapseClick : function(){
32228         this[this.collapsed ? "expand" : "collapse"]();
32229     },
32230
32231     /**
32232      * Collapses the dialog to its minimized state (only the title bar is visible).
32233      * Equivalent to the user clicking the collapse dialog button.
32234      */
32235     collapse : function(){
32236         if(!this.collapsed){
32237             this.collapsed = true;
32238             this.el.addClass("x-dlg-collapsed");
32239             this.restoreHeight = this.el.getHeight();
32240             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32241         }
32242     },
32243
32244     /**
32245      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32246      * clicking the expand dialog button.
32247      */
32248     expand : function(){
32249         if(this.collapsed){
32250             this.collapsed = false;
32251             this.el.removeClass("x-dlg-collapsed");
32252             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32253         }
32254     },
32255
32256     /**
32257      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32258      * @return {Roo.TabPanel} The tabs component
32259      */
32260     initTabs : function(){
32261         var tabs = this.getTabs();
32262         while(tabs.getTab(0)){
32263             tabs.removeTab(0);
32264         }
32265         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32266             var dom = el.dom;
32267             tabs.addTab(Roo.id(dom), dom.title);
32268             dom.title = "";
32269         });
32270         tabs.activate(0);
32271         return tabs;
32272     },
32273
32274     // private
32275     beforeResize : function(){
32276         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32277     },
32278
32279     // private
32280     onResize : function(){
32281         this.refreshSize();
32282         this.syncBodyHeight();
32283         this.adjustAssets();
32284         this.focus();
32285         this.fireEvent("resize", this, this.size.width, this.size.height);
32286     },
32287
32288     // private
32289     onKeyDown : function(e){
32290         if(this.isVisible()){
32291             this.fireEvent("keydown", this, e);
32292         }
32293     },
32294
32295     /**
32296      * Resizes the dialog.
32297      * @param {Number} width
32298      * @param {Number} height
32299      * @return {Roo.BasicDialog} this
32300      */
32301     resizeTo : function(width, height){
32302         this.el.setSize(width, height);
32303         this.size = {width: width, height: height};
32304         this.syncBodyHeight();
32305         if(this.fixedcenter){
32306             this.center();
32307         }
32308         if(this.isVisible()){
32309             this.constrainXY();
32310             this.adjustAssets();
32311         }
32312         this.fireEvent("resize", this, width, height);
32313         return this;
32314     },
32315
32316
32317     /**
32318      * Resizes the dialog to fit the specified content size.
32319      * @param {Number} width
32320      * @param {Number} height
32321      * @return {Roo.BasicDialog} this
32322      */
32323     setContentSize : function(w, h){
32324         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32325         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32326         //if(!this.el.isBorderBox()){
32327             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32328             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32329         //}
32330         if(this.tabs){
32331             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32332             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32333         }
32334         this.resizeTo(w, h);
32335         return this;
32336     },
32337
32338     /**
32339      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32340      * executed in response to a particular key being pressed while the dialog is active.
32341      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32342      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32343      * @param {Function} fn The function to call
32344      * @param {Object} scope (optional) The scope of the function
32345      * @return {Roo.BasicDialog} this
32346      */
32347     addKeyListener : function(key, fn, scope){
32348         var keyCode, shift, ctrl, alt;
32349         if(typeof key == "object" && !(key instanceof Array)){
32350             keyCode = key["key"];
32351             shift = key["shift"];
32352             ctrl = key["ctrl"];
32353             alt = key["alt"];
32354         }else{
32355             keyCode = key;
32356         }
32357         var handler = function(dlg, e){
32358             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32359                 var k = e.getKey();
32360                 if(keyCode instanceof Array){
32361                     for(var i = 0, len = keyCode.length; i < len; i++){
32362                         if(keyCode[i] == k){
32363                           fn.call(scope || window, dlg, k, e);
32364                           return;
32365                         }
32366                     }
32367                 }else{
32368                     if(k == keyCode){
32369                         fn.call(scope || window, dlg, k, e);
32370                     }
32371                 }
32372             }
32373         };
32374         this.on("keydown", handler);
32375         return this;
32376     },
32377
32378     /**
32379      * Returns the TabPanel component (creates it if it doesn't exist).
32380      * Note: If you wish to simply check for the existence of tabs without creating them,
32381      * check for a null 'tabs' property.
32382      * @return {Roo.TabPanel} The tabs component
32383      */
32384     getTabs : function(){
32385         if(!this.tabs){
32386             this.el.addClass("x-dlg-auto-tabs");
32387             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32388             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32389         }
32390         return this.tabs;
32391     },
32392
32393     /**
32394      * Adds a button to the footer section of the dialog.
32395      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32396      * object or a valid Roo.DomHelper element config
32397      * @param {Function} handler The function called when the button is clicked
32398      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32399      * @return {Roo.Button} The new button
32400      */
32401     addButton : function(config, handler, scope){
32402         var dh = Roo.DomHelper;
32403         if(!this.footer){
32404             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32405         }
32406         if(!this.btnContainer){
32407             var tb = this.footer.createChild({
32408
32409                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32410                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32411             }, null, true);
32412             this.btnContainer = tb.firstChild.firstChild.firstChild;
32413         }
32414         var bconfig = {
32415             handler: handler,
32416             scope: scope,
32417             minWidth: this.minButtonWidth,
32418             hideParent:true
32419         };
32420         if(typeof config == "string"){
32421             bconfig.text = config;
32422         }else{
32423             if(config.tag){
32424                 bconfig.dhconfig = config;
32425             }else{
32426                 Roo.apply(bconfig, config);
32427             }
32428         }
32429         var fc = false;
32430         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32431             bconfig.position = Math.max(0, bconfig.position);
32432             fc = this.btnContainer.childNodes[bconfig.position];
32433         }
32434          
32435         var btn = new Roo.Button(
32436             fc ? 
32437                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32438                 : this.btnContainer.appendChild(document.createElement("td")),
32439             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32440             bconfig
32441         );
32442         this.syncBodyHeight();
32443         if(!this.buttons){
32444             /**
32445              * Array of all the buttons that have been added to this dialog via addButton
32446              * @type Array
32447              */
32448             this.buttons = [];
32449         }
32450         this.buttons.push(btn);
32451         return btn;
32452     },
32453
32454     /**
32455      * Sets the default button to be focused when the dialog is displayed.
32456      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32457      * @return {Roo.BasicDialog} this
32458      */
32459     setDefaultButton : function(btn){
32460         this.defaultButton = btn;
32461         return this;
32462     },
32463
32464     // private
32465     getHeaderFooterHeight : function(safe){
32466         var height = 0;
32467         if(this.header){
32468            height += this.header.getHeight();
32469         }
32470         if(this.footer){
32471            var fm = this.footer.getMargins();
32472             height += (this.footer.getHeight()+fm.top+fm.bottom);
32473         }
32474         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32475         height += this.centerBg.getPadding("tb");
32476         return height;
32477     },
32478
32479     // private
32480     syncBodyHeight : function()
32481     {
32482         var bd = this.body, // the text
32483             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32484             bw = this.bwrap;
32485         var height = this.size.height - this.getHeaderFooterHeight(false);
32486         bd.setHeight(height-bd.getMargins("tb"));
32487         var hh = this.header.getHeight();
32488         var h = this.size.height-hh;
32489         cb.setHeight(h);
32490         
32491         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32492         bw.setHeight(h-cb.getPadding("tb"));
32493         
32494         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32495         bd.setWidth(bw.getWidth(true));
32496         if(this.tabs){
32497             this.tabs.syncHeight();
32498             if(Roo.isIE){
32499                 this.tabs.el.repaint();
32500             }
32501         }
32502     },
32503
32504     /**
32505      * Restores the previous state of the dialog if Roo.state is configured.
32506      * @return {Roo.BasicDialog} this
32507      */
32508     restoreState : function(){
32509         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32510         if(box && box.width){
32511             this.xy = [box.x, box.y];
32512             this.resizeTo(box.width, box.height);
32513         }
32514         return this;
32515     },
32516
32517     // private
32518     beforeShow : function(){
32519         this.expand();
32520         if(this.fixedcenter){
32521             this.xy = this.el.getCenterXY(true);
32522         }
32523         if(this.modal){
32524             Roo.get(document.body).addClass("x-body-masked");
32525             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32526             this.mask.show();
32527         }
32528         this.constrainXY();
32529     },
32530
32531     // private
32532     animShow : function(){
32533         var b = Roo.get(this.animateTarget).getBox();
32534         this.proxy.setSize(b.width, b.height);
32535         this.proxy.setLocation(b.x, b.y);
32536         this.proxy.show();
32537         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32538                     true, .35, this.showEl.createDelegate(this));
32539     },
32540
32541     /**
32542      * Shows the dialog.
32543      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32544      * @return {Roo.BasicDialog} this
32545      */
32546     show : function(animateTarget){
32547         if (this.fireEvent("beforeshow", this) === false){
32548             return;
32549         }
32550         if(this.syncHeightBeforeShow){
32551             this.syncBodyHeight();
32552         }else if(this.firstShow){
32553             this.firstShow = false;
32554             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32555         }
32556         this.animateTarget = animateTarget || this.animateTarget;
32557         if(!this.el.isVisible()){
32558             this.beforeShow();
32559             if(this.animateTarget && Roo.get(this.animateTarget)){
32560                 this.animShow();
32561             }else{
32562                 this.showEl();
32563             }
32564         }
32565         return this;
32566     },
32567
32568     // private
32569     showEl : function(){
32570         this.proxy.hide();
32571         this.el.setXY(this.xy);
32572         this.el.show();
32573         this.adjustAssets(true);
32574         this.toFront();
32575         this.focus();
32576         // IE peekaboo bug - fix found by Dave Fenwick
32577         if(Roo.isIE){
32578             this.el.repaint();
32579         }
32580         this.fireEvent("show", this);
32581     },
32582
32583     /**
32584      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32585      * dialog itself will receive focus.
32586      */
32587     focus : function(){
32588         if(this.defaultButton){
32589             this.defaultButton.focus();
32590         }else{
32591             this.focusEl.focus();
32592         }
32593     },
32594
32595     // private
32596     constrainXY : function(){
32597         if(this.constraintoviewport !== false){
32598             if(!this.viewSize){
32599                 if(this.container){
32600                     var s = this.container.getSize();
32601                     this.viewSize = [s.width, s.height];
32602                 }else{
32603                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32604                 }
32605             }
32606             var s = Roo.get(this.container||document).getScroll();
32607
32608             var x = this.xy[0], y = this.xy[1];
32609             var w = this.size.width, h = this.size.height;
32610             var vw = this.viewSize[0], vh = this.viewSize[1];
32611             // only move it if it needs it
32612             var moved = false;
32613             // first validate right/bottom
32614             if(x + w > vw+s.left){
32615                 x = vw - w;
32616                 moved = true;
32617             }
32618             if(y + h > vh+s.top){
32619                 y = vh - h;
32620                 moved = true;
32621             }
32622             // then make sure top/left isn't negative
32623             if(x < s.left){
32624                 x = s.left;
32625                 moved = true;
32626             }
32627             if(y < s.top){
32628                 y = s.top;
32629                 moved = true;
32630             }
32631             if(moved){
32632                 // cache xy
32633                 this.xy = [x, y];
32634                 if(this.isVisible()){
32635                     this.el.setLocation(x, y);
32636                     this.adjustAssets();
32637                 }
32638             }
32639         }
32640     },
32641
32642     // private
32643     onDrag : function(){
32644         if(!this.proxyDrag){
32645             this.xy = this.el.getXY();
32646             this.adjustAssets();
32647         }
32648     },
32649
32650     // private
32651     adjustAssets : function(doShow){
32652         var x = this.xy[0], y = this.xy[1];
32653         var w = this.size.width, h = this.size.height;
32654         if(doShow === true){
32655             if(this.shadow){
32656                 this.shadow.show(this.el);
32657             }
32658             if(this.shim){
32659                 this.shim.show();
32660             }
32661         }
32662         if(this.shadow && this.shadow.isVisible()){
32663             this.shadow.show(this.el);
32664         }
32665         if(this.shim && this.shim.isVisible()){
32666             this.shim.setBounds(x, y, w, h);
32667         }
32668     },
32669
32670     // private
32671     adjustViewport : function(w, h){
32672         if(!w || !h){
32673             w = Roo.lib.Dom.getViewWidth();
32674             h = Roo.lib.Dom.getViewHeight();
32675         }
32676         // cache the size
32677         this.viewSize = [w, h];
32678         if(this.modal && this.mask.isVisible()){
32679             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32680             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32681         }
32682         if(this.isVisible()){
32683             this.constrainXY();
32684         }
32685     },
32686
32687     /**
32688      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32689      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32690      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32691      */
32692     destroy : function(removeEl){
32693         if(this.isVisible()){
32694             this.animateTarget = null;
32695             this.hide();
32696         }
32697         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32698         if(this.tabs){
32699             this.tabs.destroy(removeEl);
32700         }
32701         Roo.destroy(
32702              this.shim,
32703              this.proxy,
32704              this.resizer,
32705              this.close,
32706              this.mask
32707         );
32708         if(this.dd){
32709             this.dd.unreg();
32710         }
32711         if(this.buttons){
32712            for(var i = 0, len = this.buttons.length; i < len; i++){
32713                this.buttons[i].destroy();
32714            }
32715         }
32716         this.el.removeAllListeners();
32717         if(removeEl === true){
32718             this.el.update("");
32719             this.el.remove();
32720         }
32721         Roo.DialogManager.unregister(this);
32722     },
32723
32724     // private
32725     startMove : function(){
32726         if(this.proxyDrag){
32727             this.proxy.show();
32728         }
32729         if(this.constraintoviewport !== false){
32730             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32731         }
32732     },
32733
32734     // private
32735     endMove : function(){
32736         if(!this.proxyDrag){
32737             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32738         }else{
32739             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32740             this.proxy.hide();
32741         }
32742         this.refreshSize();
32743         this.adjustAssets();
32744         this.focus();
32745         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32746     },
32747
32748     /**
32749      * Brings this dialog to the front of any other visible dialogs
32750      * @return {Roo.BasicDialog} this
32751      */
32752     toFront : function(){
32753         Roo.DialogManager.bringToFront(this);
32754         return this;
32755     },
32756
32757     /**
32758      * Sends this dialog to the back (under) of any other visible dialogs
32759      * @return {Roo.BasicDialog} this
32760      */
32761     toBack : function(){
32762         Roo.DialogManager.sendToBack(this);
32763         return this;
32764     },
32765
32766     /**
32767      * Centers this dialog in the viewport
32768      * @return {Roo.BasicDialog} this
32769      */
32770     center : function(){
32771         var xy = this.el.getCenterXY(true);
32772         this.moveTo(xy[0], xy[1]);
32773         return this;
32774     },
32775
32776     /**
32777      * Moves the dialog's top-left corner to the specified point
32778      * @param {Number} x
32779      * @param {Number} y
32780      * @return {Roo.BasicDialog} this
32781      */
32782     moveTo : function(x, y){
32783         this.xy = [x,y];
32784         if(this.isVisible()){
32785             this.el.setXY(this.xy);
32786             this.adjustAssets();
32787         }
32788         return this;
32789     },
32790
32791     /**
32792      * Aligns the dialog to the specified element
32793      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32794      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32795      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32796      * @return {Roo.BasicDialog} this
32797      */
32798     alignTo : function(element, position, offsets){
32799         this.xy = this.el.getAlignToXY(element, position, offsets);
32800         if(this.isVisible()){
32801             this.el.setXY(this.xy);
32802             this.adjustAssets();
32803         }
32804         return this;
32805     },
32806
32807     /**
32808      * Anchors an element to another element and realigns it when the window is resized.
32809      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32810      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32811      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32812      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32813      * is a number, it is used as the buffer delay (defaults to 50ms).
32814      * @return {Roo.BasicDialog} this
32815      */
32816     anchorTo : function(el, alignment, offsets, monitorScroll){
32817         var action = function(){
32818             this.alignTo(el, alignment, offsets);
32819         };
32820         Roo.EventManager.onWindowResize(action, this);
32821         var tm = typeof monitorScroll;
32822         if(tm != 'undefined'){
32823             Roo.EventManager.on(window, 'scroll', action, this,
32824                 {buffer: tm == 'number' ? monitorScroll : 50});
32825         }
32826         action.call(this);
32827         return this;
32828     },
32829
32830     /**
32831      * Returns true if the dialog is visible
32832      * @return {Boolean}
32833      */
32834     isVisible : function(){
32835         return this.el.isVisible();
32836     },
32837
32838     // private
32839     animHide : function(callback){
32840         var b = Roo.get(this.animateTarget).getBox();
32841         this.proxy.show();
32842         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32843         this.el.hide();
32844         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32845                     this.hideEl.createDelegate(this, [callback]));
32846     },
32847
32848     /**
32849      * Hides the dialog.
32850      * @param {Function} callback (optional) Function to call when the dialog is hidden
32851      * @return {Roo.BasicDialog} this
32852      */
32853     hide : function(callback){
32854         if (this.fireEvent("beforehide", this) === false){
32855             return;
32856         }
32857         if(this.shadow){
32858             this.shadow.hide();
32859         }
32860         if(this.shim) {
32861           this.shim.hide();
32862         }
32863         // sometimes animateTarget seems to get set.. causing problems...
32864         // this just double checks..
32865         if(this.animateTarget && Roo.get(this.animateTarget)) {
32866            this.animHide(callback);
32867         }else{
32868             this.el.hide();
32869             this.hideEl(callback);
32870         }
32871         return this;
32872     },
32873
32874     // private
32875     hideEl : function(callback){
32876         this.proxy.hide();
32877         if(this.modal){
32878             this.mask.hide();
32879             Roo.get(document.body).removeClass("x-body-masked");
32880         }
32881         this.fireEvent("hide", this);
32882         if(typeof callback == "function"){
32883             callback();
32884         }
32885     },
32886
32887     // private
32888     hideAction : function(){
32889         this.setLeft("-10000px");
32890         this.setTop("-10000px");
32891         this.setStyle("visibility", "hidden");
32892     },
32893
32894     // private
32895     refreshSize : function(){
32896         this.size = this.el.getSize();
32897         this.xy = this.el.getXY();
32898         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32899     },
32900
32901     // private
32902     // z-index is managed by the DialogManager and may be overwritten at any time
32903     setZIndex : function(index){
32904         if(this.modal){
32905             this.mask.setStyle("z-index", index);
32906         }
32907         if(this.shim){
32908             this.shim.setStyle("z-index", ++index);
32909         }
32910         if(this.shadow){
32911             this.shadow.setZIndex(++index);
32912         }
32913         this.el.setStyle("z-index", ++index);
32914         if(this.proxy){
32915             this.proxy.setStyle("z-index", ++index);
32916         }
32917         if(this.resizer){
32918             this.resizer.proxy.setStyle("z-index", ++index);
32919         }
32920
32921         this.lastZIndex = index;
32922     },
32923
32924     /**
32925      * Returns the element for this dialog
32926      * @return {Roo.Element} The underlying dialog Element
32927      */
32928     getEl : function(){
32929         return this.el;
32930     }
32931 });
32932
32933 /**
32934  * @class Roo.DialogManager
32935  * Provides global access to BasicDialogs that have been created and
32936  * support for z-indexing (layering) multiple open dialogs.
32937  */
32938 Roo.DialogManager = function(){
32939     var list = {};
32940     var accessList = [];
32941     var front = null;
32942
32943     // private
32944     var sortDialogs = function(d1, d2){
32945         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32946     };
32947
32948     // private
32949     var orderDialogs = function(){
32950         accessList.sort(sortDialogs);
32951         var seed = Roo.DialogManager.zseed;
32952         for(var i = 0, len = accessList.length; i < len; i++){
32953             var dlg = accessList[i];
32954             if(dlg){
32955                 dlg.setZIndex(seed + (i*10));
32956             }
32957         }
32958     };
32959
32960     return {
32961         /**
32962          * The starting z-index for BasicDialogs (defaults to 9000)
32963          * @type Number The z-index value
32964          */
32965         zseed : 9000,
32966
32967         // private
32968         register : function(dlg){
32969             list[dlg.id] = dlg;
32970             accessList.push(dlg);
32971         },
32972
32973         // private
32974         unregister : function(dlg){
32975             delete list[dlg.id];
32976             var i=0;
32977             var len=0;
32978             if(!accessList.indexOf){
32979                 for(  i = 0, len = accessList.length; i < len; i++){
32980                     if(accessList[i] == dlg){
32981                         accessList.splice(i, 1);
32982                         return;
32983                     }
32984                 }
32985             }else{
32986                  i = accessList.indexOf(dlg);
32987                 if(i != -1){
32988                     accessList.splice(i, 1);
32989                 }
32990             }
32991         },
32992
32993         /**
32994          * Gets a registered dialog by id
32995          * @param {String/Object} id The id of the dialog or a dialog
32996          * @return {Roo.BasicDialog} this
32997          */
32998         get : function(id){
32999             return typeof id == "object" ? id : list[id];
33000         },
33001
33002         /**
33003          * Brings the specified dialog to the front
33004          * @param {String/Object} dlg The id of the dialog or a dialog
33005          * @return {Roo.BasicDialog} this
33006          */
33007         bringToFront : function(dlg){
33008             dlg = this.get(dlg);
33009             if(dlg != front){
33010                 front = dlg;
33011                 dlg._lastAccess = new Date().getTime();
33012                 orderDialogs();
33013             }
33014             return dlg;
33015         },
33016
33017         /**
33018          * Sends the specified dialog to the back
33019          * @param {String/Object} dlg The id of the dialog or a dialog
33020          * @return {Roo.BasicDialog} this
33021          */
33022         sendToBack : function(dlg){
33023             dlg = this.get(dlg);
33024             dlg._lastAccess = -(new Date().getTime());
33025             orderDialogs();
33026             return dlg;
33027         },
33028
33029         /**
33030          * Hides all dialogs
33031          */
33032         hideAll : function(){
33033             for(var id in list){
33034                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33035                     list[id].hide();
33036                 }
33037             }
33038         }
33039     };
33040 }();
33041
33042 /**
33043  * @class Roo.LayoutDialog
33044  * @extends Roo.BasicDialog
33045  * Dialog which provides adjustments for working with a layout in a Dialog.
33046  * Add your necessary layout config options to the dialog's config.<br>
33047  * Example usage (including a nested layout):
33048  * <pre><code>
33049 if(!dialog){
33050     dialog = new Roo.LayoutDialog("download-dlg", {
33051         modal: true,
33052         width:600,
33053         height:450,
33054         shadow:true,
33055         minWidth:500,
33056         minHeight:350,
33057         autoTabs:true,
33058         proxyDrag:true,
33059         // layout config merges with the dialog config
33060         center:{
33061             tabPosition: "top",
33062             alwaysShowTabs: true
33063         }
33064     });
33065     dialog.addKeyListener(27, dialog.hide, dialog);
33066     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33067     dialog.addButton("Build It!", this.getDownload, this);
33068
33069     // we can even add nested layouts
33070     var innerLayout = new Roo.BorderLayout("dl-inner", {
33071         east: {
33072             initialSize: 200,
33073             autoScroll:true,
33074             split:true
33075         },
33076         center: {
33077             autoScroll:true
33078         }
33079     });
33080     innerLayout.beginUpdate();
33081     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33082     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33083     innerLayout.endUpdate(true);
33084
33085     var layout = dialog.getLayout();
33086     layout.beginUpdate();
33087     layout.add("center", new Roo.ContentPanel("standard-panel",
33088                         {title: "Download the Source", fitToFrame:true}));
33089     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33090                {title: "Build your own roo.js"}));
33091     layout.getRegion("center").showPanel(sp);
33092     layout.endUpdate();
33093 }
33094 </code></pre>
33095     * @constructor
33096     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33097     * @param {Object} config configuration options
33098   */
33099 Roo.LayoutDialog = function(el, cfg){
33100     
33101     var config=  cfg;
33102     if (typeof(cfg) == 'undefined') {
33103         config = Roo.apply({}, el);
33104         // not sure why we use documentElement here.. - it should always be body.
33105         // IE7 borks horribly if we use documentElement.
33106         // webkit also does not like documentElement - it creates a body element...
33107         el = Roo.get( document.body || document.documentElement ).createChild();
33108         //config.autoCreate = true;
33109     }
33110     
33111     
33112     config.autoTabs = false;
33113     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33114     this.body.setStyle({overflow:"hidden", position:"relative"});
33115     this.layout = new Roo.BorderLayout(this.body.dom, config);
33116     this.layout.monitorWindowResize = false;
33117     this.el.addClass("x-dlg-auto-layout");
33118     // fix case when center region overwrites center function
33119     this.center = Roo.BasicDialog.prototype.center;
33120     this.on("show", this.layout.layout, this.layout, true);
33121     if (config.items) {
33122         var xitems = config.items;
33123         delete config.items;
33124         Roo.each(xitems, this.addxtype, this);
33125     }
33126     
33127     
33128 };
33129 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33130     /**
33131      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33132      * @deprecated
33133      */
33134     endUpdate : function(){
33135         this.layout.endUpdate();
33136     },
33137
33138     /**
33139      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33140      *  @deprecated
33141      */
33142     beginUpdate : function(){
33143         this.layout.beginUpdate();
33144     },
33145
33146     /**
33147      * Get the BorderLayout for this dialog
33148      * @return {Roo.BorderLayout}
33149      */
33150     getLayout : function(){
33151         return this.layout;
33152     },
33153
33154     showEl : function(){
33155         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33156         if(Roo.isIE7){
33157             this.layout.layout();
33158         }
33159     },
33160
33161     // private
33162     // Use the syncHeightBeforeShow config option to control this automatically
33163     syncBodyHeight : function(){
33164         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33165         if(this.layout){this.layout.layout();}
33166     },
33167     
33168       /**
33169      * Add an xtype element (actually adds to the layout.)
33170      * @return {Object} xdata xtype object data.
33171      */
33172     
33173     addxtype : function(c) {
33174         return this.layout.addxtype(c);
33175     }
33176 });/*
33177  * Based on:
33178  * Ext JS Library 1.1.1
33179  * Copyright(c) 2006-2007, Ext JS, LLC.
33180  *
33181  * Originally Released Under LGPL - original licence link has changed is not relivant.
33182  *
33183  * Fork - LGPL
33184  * <script type="text/javascript">
33185  */
33186  
33187 /**
33188  * @class Roo.MessageBox
33189  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33190  * Example usage:
33191  *<pre><code>
33192 // Basic alert:
33193 Roo.Msg.alert('Status', 'Changes saved successfully.');
33194
33195 // Prompt for user data:
33196 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33197     if (btn == 'ok'){
33198         // process text value...
33199     }
33200 });
33201
33202 // Show a dialog using config options:
33203 Roo.Msg.show({
33204    title:'Save Changes?',
33205    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33206    buttons: Roo.Msg.YESNOCANCEL,
33207    fn: processResult,
33208    animEl: 'elId'
33209 });
33210 </code></pre>
33211  * @singleton
33212  */
33213 Roo.MessageBox = function(){
33214     var dlg, opt, mask, waitTimer;
33215     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33216     var buttons, activeTextEl, bwidth;
33217
33218     // private
33219     var handleButton = function(button){
33220         dlg.hide();
33221         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33222     };
33223
33224     // private
33225     var handleHide = function(){
33226         if(opt && opt.cls){
33227             dlg.el.removeClass(opt.cls);
33228         }
33229         if(waitTimer){
33230             Roo.TaskMgr.stop(waitTimer);
33231             waitTimer = null;
33232         }
33233     };
33234
33235     // private
33236     var updateButtons = function(b){
33237         var width = 0;
33238         if(!b){
33239             buttons["ok"].hide();
33240             buttons["cancel"].hide();
33241             buttons["yes"].hide();
33242             buttons["no"].hide();
33243             dlg.footer.dom.style.display = 'none';
33244             return width;
33245         }
33246         dlg.footer.dom.style.display = '';
33247         for(var k in buttons){
33248             if(typeof buttons[k] != "function"){
33249                 if(b[k]){
33250                     buttons[k].show();
33251                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33252                     width += buttons[k].el.getWidth()+15;
33253                 }else{
33254                     buttons[k].hide();
33255                 }
33256             }
33257         }
33258         return width;
33259     };
33260
33261     // private
33262     var handleEsc = function(d, k, e){
33263         if(opt && opt.closable !== false){
33264             dlg.hide();
33265         }
33266         if(e){
33267             e.stopEvent();
33268         }
33269     };
33270
33271     return {
33272         /**
33273          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33274          * @return {Roo.BasicDialog} The BasicDialog element
33275          */
33276         getDialog : function(){
33277            if(!dlg){
33278                 dlg = new Roo.BasicDialog("x-msg-box", {
33279                     autoCreate : true,
33280                     shadow: true,
33281                     draggable: true,
33282                     resizable:false,
33283                     constraintoviewport:false,
33284                     fixedcenter:true,
33285                     collapsible : false,
33286                     shim:true,
33287                     modal: true,
33288                     width:400, height:100,
33289                     buttonAlign:"center",
33290                     closeClick : function(){
33291                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33292                             handleButton("no");
33293                         }else{
33294                             handleButton("cancel");
33295                         }
33296                     }
33297                 });
33298                 dlg.on("hide", handleHide);
33299                 mask = dlg.mask;
33300                 dlg.addKeyListener(27, handleEsc);
33301                 buttons = {};
33302                 var bt = this.buttonText;
33303                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33304                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33305                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33306                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33307                 bodyEl = dlg.body.createChild({
33308
33309                     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>'
33310                 });
33311                 msgEl = bodyEl.dom.firstChild;
33312                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33313                 textboxEl.enableDisplayMode();
33314                 textboxEl.addKeyListener([10,13], function(){
33315                     if(dlg.isVisible() && opt && opt.buttons){
33316                         if(opt.buttons.ok){
33317                             handleButton("ok");
33318                         }else if(opt.buttons.yes){
33319                             handleButton("yes");
33320                         }
33321                     }
33322                 });
33323                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33324                 textareaEl.enableDisplayMode();
33325                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33326                 progressEl.enableDisplayMode();
33327                 var pf = progressEl.dom.firstChild;
33328                 if (pf) {
33329                     pp = Roo.get(pf.firstChild);
33330                     pp.setHeight(pf.offsetHeight);
33331                 }
33332                 
33333             }
33334             return dlg;
33335         },
33336
33337         /**
33338          * Updates the message box body text
33339          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33340          * the XHTML-compliant non-breaking space character '&amp;#160;')
33341          * @return {Roo.MessageBox} This message box
33342          */
33343         updateText : function(text){
33344             if(!dlg.isVisible() && !opt.width){
33345                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33346             }
33347             msgEl.innerHTML = text || '&#160;';
33348       
33349             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33350             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33351             var w = Math.max(
33352                     Math.min(opt.width || cw , this.maxWidth), 
33353                     Math.max(opt.minWidth || this.minWidth, bwidth)
33354             );
33355             if(opt.prompt){
33356                 activeTextEl.setWidth(w);
33357             }
33358             if(dlg.isVisible()){
33359                 dlg.fixedcenter = false;
33360             }
33361             // to big, make it scroll. = But as usual stupid IE does not support
33362             // !important..
33363             
33364             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33365                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33366                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33367             } else {
33368                 bodyEl.dom.style.height = '';
33369                 bodyEl.dom.style.overflowY = '';
33370             }
33371             if (cw > w) {
33372                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33373             } else {
33374                 bodyEl.dom.style.overflowX = '';
33375             }
33376             
33377             dlg.setContentSize(w, bodyEl.getHeight());
33378             if(dlg.isVisible()){
33379                 dlg.fixedcenter = true;
33380             }
33381             return this;
33382         },
33383
33384         /**
33385          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33386          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33387          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33388          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33389          * @return {Roo.MessageBox} This message box
33390          */
33391         updateProgress : function(value, text){
33392             if(text){
33393                 this.updateText(text);
33394             }
33395             if (pp) { // weird bug on my firefox - for some reason this is not defined
33396                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33397             }
33398             return this;
33399         },        
33400
33401         /**
33402          * Returns true if the message box is currently displayed
33403          * @return {Boolean} True if the message box is visible, else false
33404          */
33405         isVisible : function(){
33406             return dlg && dlg.isVisible();  
33407         },
33408
33409         /**
33410          * Hides the message box if it is displayed
33411          */
33412         hide : function(){
33413             if(this.isVisible()){
33414                 dlg.hide();
33415             }  
33416         },
33417
33418         /**
33419          * Displays a new message box, or reinitializes an existing message box, based on the config options
33420          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33421          * The following config object properties are supported:
33422          * <pre>
33423 Property    Type             Description
33424 ----------  ---------------  ------------------------------------------------------------------------------------
33425 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33426                                    closes (defaults to undefined)
33427 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33428                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33429 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33430                                    progress and wait dialogs will ignore this property and always hide the
33431                                    close button as they can only be closed programmatically.
33432 cls               String           A custom CSS class to apply to the message box element
33433 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33434                                    displayed (defaults to 75)
33435 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33436                                    function will be btn (the name of the button that was clicked, if applicable,
33437                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33438                                    Progress and wait dialogs will ignore this option since they do not respond to
33439                                    user actions and can only be closed programmatically, so any required function
33440                                    should be called by the same code after it closes the dialog.
33441 icon              String           A CSS class that provides a background image to be used as an icon for
33442                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33443 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33444 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33445 modal             Boolean          False to allow user interaction with the page while the message box is
33446                                    displayed (defaults to true)
33447 msg               String           A string that will replace the existing message box body text (defaults
33448                                    to the XHTML-compliant non-breaking space character '&#160;')
33449 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33450 progress          Boolean          True to display a progress bar (defaults to false)
33451 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33452 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33453 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33454 title             String           The title text
33455 value             String           The string value to set into the active textbox element if displayed
33456 wait              Boolean          True to display a progress bar (defaults to false)
33457 width             Number           The width of the dialog in pixels
33458 </pre>
33459          *
33460          * Example usage:
33461          * <pre><code>
33462 Roo.Msg.show({
33463    title: 'Address',
33464    msg: 'Please enter your address:',
33465    width: 300,
33466    buttons: Roo.MessageBox.OKCANCEL,
33467    multiline: true,
33468    fn: saveAddress,
33469    animEl: 'addAddressBtn'
33470 });
33471 </code></pre>
33472          * @param {Object} config Configuration options
33473          * @return {Roo.MessageBox} This message box
33474          */
33475         show : function(options)
33476         {
33477             
33478             // this causes nightmares if you show one dialog after another
33479             // especially on callbacks..
33480              
33481             if(this.isVisible()){
33482                 
33483                 this.hide();
33484                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33485                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33486                 Roo.log("New Dialog Message:" +  options.msg )
33487                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33488                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33489                 
33490             }
33491             var d = this.getDialog();
33492             opt = options;
33493             d.setTitle(opt.title || "&#160;");
33494             d.close.setDisplayed(opt.closable !== false);
33495             activeTextEl = textboxEl;
33496             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33497             if(opt.prompt){
33498                 if(opt.multiline){
33499                     textboxEl.hide();
33500                     textareaEl.show();
33501                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33502                         opt.multiline : this.defaultTextHeight);
33503                     activeTextEl = textareaEl;
33504                 }else{
33505                     textboxEl.show();
33506                     textareaEl.hide();
33507                 }
33508             }else{
33509                 textboxEl.hide();
33510                 textareaEl.hide();
33511             }
33512             progressEl.setDisplayed(opt.progress === true);
33513             this.updateProgress(0);
33514             activeTextEl.dom.value = opt.value || "";
33515             if(opt.prompt){
33516                 dlg.setDefaultButton(activeTextEl);
33517             }else{
33518                 var bs = opt.buttons;
33519                 var db = null;
33520                 if(bs && bs.ok){
33521                     db = buttons["ok"];
33522                 }else if(bs && bs.yes){
33523                     db = buttons["yes"];
33524                 }
33525                 dlg.setDefaultButton(db);
33526             }
33527             bwidth = updateButtons(opt.buttons);
33528             this.updateText(opt.msg);
33529             if(opt.cls){
33530                 d.el.addClass(opt.cls);
33531             }
33532             d.proxyDrag = opt.proxyDrag === true;
33533             d.modal = opt.modal !== false;
33534             d.mask = opt.modal !== false ? mask : false;
33535             if(!d.isVisible()){
33536                 // force it to the end of the z-index stack so it gets a cursor in FF
33537                 document.body.appendChild(dlg.el.dom);
33538                 d.animateTarget = null;
33539                 d.show(options.animEl);
33540             }
33541             return this;
33542         },
33543
33544         /**
33545          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33546          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33547          * and closing the message box when the process is complete.
33548          * @param {String} title The title bar text
33549          * @param {String} msg The message box body text
33550          * @return {Roo.MessageBox} This message box
33551          */
33552         progress : function(title, msg){
33553             this.show({
33554                 title : title,
33555                 msg : msg,
33556                 buttons: false,
33557                 progress:true,
33558                 closable:false,
33559                 minWidth: this.minProgressWidth,
33560                 modal : true
33561             });
33562             return this;
33563         },
33564
33565         /**
33566          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33567          * If a callback function is passed it will be called after the user clicks the button, and the
33568          * id of the button that was clicked will be passed as the only parameter to the callback
33569          * (could also be the top-right close button).
33570          * @param {String} title The title bar text
33571          * @param {String} msg The message box body text
33572          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33573          * @param {Object} scope (optional) The scope of the callback function
33574          * @return {Roo.MessageBox} This message box
33575          */
33576         alert : function(title, msg, fn, scope){
33577             this.show({
33578                 title : title,
33579                 msg : msg,
33580                 buttons: this.OK,
33581                 fn: fn,
33582                 scope : scope,
33583                 modal : true
33584             });
33585             return this;
33586         },
33587
33588         /**
33589          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33590          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33591          * You are responsible for closing the message box when the process is complete.
33592          * @param {String} msg The message box body text
33593          * @param {String} title (optional) The title bar text
33594          * @return {Roo.MessageBox} This message box
33595          */
33596         wait : function(msg, title){
33597             this.show({
33598                 title : title,
33599                 msg : msg,
33600                 buttons: false,
33601                 closable:false,
33602                 progress:true,
33603                 modal:true,
33604                 width:300,
33605                 wait:true
33606             });
33607             waitTimer = Roo.TaskMgr.start({
33608                 run: function(i){
33609                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33610                 },
33611                 interval: 1000
33612             });
33613             return this;
33614         },
33615
33616         /**
33617          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33618          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33619          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33620          * @param {String} title The title bar text
33621          * @param {String} msg The message box body text
33622          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33623          * @param {Object} scope (optional) The scope of the callback function
33624          * @return {Roo.MessageBox} This message box
33625          */
33626         confirm : function(title, msg, fn, scope){
33627             this.show({
33628                 title : title,
33629                 msg : msg,
33630                 buttons: this.YESNO,
33631                 fn: fn,
33632                 scope : scope,
33633                 modal : true
33634             });
33635             return this;
33636         },
33637
33638         /**
33639          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33640          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33641          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33642          * (could also be the top-right close button) and the text that was entered will be passed as the two
33643          * parameters to the callback.
33644          * @param {String} title The title bar text
33645          * @param {String} msg The message box body text
33646          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33647          * @param {Object} scope (optional) The scope of the callback function
33648          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33649          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33650          * @return {Roo.MessageBox} This message box
33651          */
33652         prompt : function(title, msg, fn, scope, multiline){
33653             this.show({
33654                 title : title,
33655                 msg : msg,
33656                 buttons: this.OKCANCEL,
33657                 fn: fn,
33658                 minWidth:250,
33659                 scope : scope,
33660                 prompt:true,
33661                 multiline: multiline,
33662                 modal : true
33663             });
33664             return this;
33665         },
33666
33667         /**
33668          * Button config that displays a single OK button
33669          * @type Object
33670          */
33671         OK : {ok:true},
33672         /**
33673          * Button config that displays Yes and No buttons
33674          * @type Object
33675          */
33676         YESNO : {yes:true, no:true},
33677         /**
33678          * Button config that displays OK and Cancel buttons
33679          * @type Object
33680          */
33681         OKCANCEL : {ok:true, cancel:true},
33682         /**
33683          * Button config that displays Yes, No and Cancel buttons
33684          * @type Object
33685          */
33686         YESNOCANCEL : {yes:true, no:true, cancel:true},
33687
33688         /**
33689          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33690          * @type Number
33691          */
33692         defaultTextHeight : 75,
33693         /**
33694          * The maximum width in pixels of the message box (defaults to 600)
33695          * @type Number
33696          */
33697         maxWidth : 600,
33698         /**
33699          * The minimum width in pixels of the message box (defaults to 100)
33700          * @type Number
33701          */
33702         minWidth : 100,
33703         /**
33704          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33705          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33706          * @type Number
33707          */
33708         minProgressWidth : 250,
33709         /**
33710          * An object containing the default button text strings that can be overriden for localized language support.
33711          * Supported properties are: ok, cancel, yes and no.
33712          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33713          * @type Object
33714          */
33715         buttonText : {
33716             ok : "OK",
33717             cancel : "Cancel",
33718             yes : "Yes",
33719             no : "No"
33720         }
33721     };
33722 }();
33723
33724 /**
33725  * Shorthand for {@link Roo.MessageBox}
33726  */
33727 Roo.Msg = Roo.MessageBox;/*
33728  * Based on:
33729  * Ext JS Library 1.1.1
33730  * Copyright(c) 2006-2007, Ext JS, LLC.
33731  *
33732  * Originally Released Under LGPL - original licence link has changed is not relivant.
33733  *
33734  * Fork - LGPL
33735  * <script type="text/javascript">
33736  */
33737 /**
33738  * @class Roo.QuickTips
33739  * Provides attractive and customizable tooltips for any element.
33740  * @singleton
33741  */
33742 Roo.QuickTips = function(){
33743     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33744     var ce, bd, xy, dd;
33745     var visible = false, disabled = true, inited = false;
33746     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33747     
33748     var onOver = function(e){
33749         if(disabled){
33750             return;
33751         }
33752         var t = e.getTarget();
33753         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33754             return;
33755         }
33756         if(ce && t == ce.el){
33757             clearTimeout(hideProc);
33758             return;
33759         }
33760         if(t && tagEls[t.id]){
33761             tagEls[t.id].el = t;
33762             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33763             return;
33764         }
33765         var ttp, et = Roo.fly(t);
33766         var ns = cfg.namespace;
33767         if(tm.interceptTitles && t.title){
33768             ttp = t.title;
33769             t.qtip = ttp;
33770             t.removeAttribute("title");
33771             e.preventDefault();
33772         }else{
33773             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33774         }
33775         if(ttp){
33776             showProc = show.defer(tm.showDelay, tm, [{
33777                 el: t, 
33778                 text: ttp.replace(/\\n/g,'<br/>'),
33779                 width: et.getAttributeNS(ns, cfg.width),
33780                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33781                 title: et.getAttributeNS(ns, cfg.title),
33782                     cls: et.getAttributeNS(ns, cfg.cls)
33783             }]);
33784         }
33785     };
33786     
33787     var onOut = function(e){
33788         clearTimeout(showProc);
33789         var t = e.getTarget();
33790         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33791             hideProc = setTimeout(hide, tm.hideDelay);
33792         }
33793     };
33794     
33795     var onMove = function(e){
33796         if(disabled){
33797             return;
33798         }
33799         xy = e.getXY();
33800         xy[1] += 18;
33801         if(tm.trackMouse && ce){
33802             el.setXY(xy);
33803         }
33804     };
33805     
33806     var onDown = function(e){
33807         clearTimeout(showProc);
33808         clearTimeout(hideProc);
33809         if(!e.within(el)){
33810             if(tm.hideOnClick){
33811                 hide();
33812                 tm.disable();
33813                 tm.enable.defer(100, tm);
33814             }
33815         }
33816     };
33817     
33818     var getPad = function(){
33819         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33820     };
33821
33822     var show = function(o){
33823         if(disabled){
33824             return;
33825         }
33826         clearTimeout(dismissProc);
33827         ce = o;
33828         if(removeCls){ // in case manually hidden
33829             el.removeClass(removeCls);
33830             removeCls = null;
33831         }
33832         if(ce.cls){
33833             el.addClass(ce.cls);
33834             removeCls = ce.cls;
33835         }
33836         if(ce.title){
33837             tipTitle.update(ce.title);
33838             tipTitle.show();
33839         }else{
33840             tipTitle.update('');
33841             tipTitle.hide();
33842         }
33843         el.dom.style.width  = tm.maxWidth+'px';
33844         //tipBody.dom.style.width = '';
33845         tipBodyText.update(o.text);
33846         var p = getPad(), w = ce.width;
33847         if(!w){
33848             var td = tipBodyText.dom;
33849             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33850             if(aw > tm.maxWidth){
33851                 w = tm.maxWidth;
33852             }else if(aw < tm.minWidth){
33853                 w = tm.minWidth;
33854             }else{
33855                 w = aw;
33856             }
33857         }
33858         //tipBody.setWidth(w);
33859         el.setWidth(parseInt(w, 10) + p);
33860         if(ce.autoHide === false){
33861             close.setDisplayed(true);
33862             if(dd){
33863                 dd.unlock();
33864             }
33865         }else{
33866             close.setDisplayed(false);
33867             if(dd){
33868                 dd.lock();
33869             }
33870         }
33871         if(xy){
33872             el.avoidY = xy[1]-18;
33873             el.setXY(xy);
33874         }
33875         if(tm.animate){
33876             el.setOpacity(.1);
33877             el.setStyle("visibility", "visible");
33878             el.fadeIn({callback: afterShow});
33879         }else{
33880             afterShow();
33881         }
33882     };
33883     
33884     var afterShow = function(){
33885         if(ce){
33886             el.show();
33887             esc.enable();
33888             if(tm.autoDismiss && ce.autoHide !== false){
33889                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33890             }
33891         }
33892     };
33893     
33894     var hide = function(noanim){
33895         clearTimeout(dismissProc);
33896         clearTimeout(hideProc);
33897         ce = null;
33898         if(el.isVisible()){
33899             esc.disable();
33900             if(noanim !== true && tm.animate){
33901                 el.fadeOut({callback: afterHide});
33902             }else{
33903                 afterHide();
33904             } 
33905         }
33906     };
33907     
33908     var afterHide = function(){
33909         el.hide();
33910         if(removeCls){
33911             el.removeClass(removeCls);
33912             removeCls = null;
33913         }
33914     };
33915     
33916     return {
33917         /**
33918         * @cfg {Number} minWidth
33919         * The minimum width of the quick tip (defaults to 40)
33920         */
33921        minWidth : 40,
33922         /**
33923         * @cfg {Number} maxWidth
33924         * The maximum width of the quick tip (defaults to 300)
33925         */
33926        maxWidth : 300,
33927         /**
33928         * @cfg {Boolean} interceptTitles
33929         * True to automatically use the element's DOM title value if available (defaults to false)
33930         */
33931        interceptTitles : false,
33932         /**
33933         * @cfg {Boolean} trackMouse
33934         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33935         */
33936        trackMouse : false,
33937         /**
33938         * @cfg {Boolean} hideOnClick
33939         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33940         */
33941        hideOnClick : true,
33942         /**
33943         * @cfg {Number} showDelay
33944         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33945         */
33946        showDelay : 500,
33947         /**
33948         * @cfg {Number} hideDelay
33949         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33950         */
33951        hideDelay : 200,
33952         /**
33953         * @cfg {Boolean} autoHide
33954         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33955         * Used in conjunction with hideDelay.
33956         */
33957        autoHide : true,
33958         /**
33959         * @cfg {Boolean}
33960         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33961         * (defaults to true).  Used in conjunction with autoDismissDelay.
33962         */
33963        autoDismiss : true,
33964         /**
33965         * @cfg {Number}
33966         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33967         */
33968        autoDismissDelay : 5000,
33969        /**
33970         * @cfg {Boolean} animate
33971         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33972         */
33973        animate : false,
33974
33975        /**
33976         * @cfg {String} title
33977         * Title text to display (defaults to '').  This can be any valid HTML markup.
33978         */
33979         title: '',
33980        /**
33981         * @cfg {String} text
33982         * Body text to display (defaults to '').  This can be any valid HTML markup.
33983         */
33984         text : '',
33985        /**
33986         * @cfg {String} cls
33987         * A CSS class to apply to the base quick tip element (defaults to '').
33988         */
33989         cls : '',
33990        /**
33991         * @cfg {Number} width
33992         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33993         * minWidth or maxWidth.
33994         */
33995         width : null,
33996
33997     /**
33998      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33999      * or display QuickTips in a page.
34000      */
34001        init : function(){
34002           tm = Roo.QuickTips;
34003           cfg = tm.tagConfig;
34004           if(!inited){
34005               if(!Roo.isReady){ // allow calling of init() before onReady
34006                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34007                   return;
34008               }
34009               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34010               el.fxDefaults = {stopFx: true};
34011               // maximum custom styling
34012               //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>');
34013               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>');              
34014               tipTitle = el.child('h3');
34015               tipTitle.enableDisplayMode("block");
34016               tipBody = el.child('div.x-tip-bd');
34017               tipBodyText = el.child('div.x-tip-bd-inner');
34018               //bdLeft = el.child('div.x-tip-bd-left');
34019               //bdRight = el.child('div.x-tip-bd-right');
34020               close = el.child('div.x-tip-close');
34021               close.enableDisplayMode("block");
34022               close.on("click", hide);
34023               var d = Roo.get(document);
34024               d.on("mousedown", onDown);
34025               d.on("mouseover", onOver);
34026               d.on("mouseout", onOut);
34027               d.on("mousemove", onMove);
34028               esc = d.addKeyListener(27, hide);
34029               esc.disable();
34030               if(Roo.dd.DD){
34031                   dd = el.initDD("default", null, {
34032                       onDrag : function(){
34033                           el.sync();  
34034                       }
34035                   });
34036                   dd.setHandleElId(tipTitle.id);
34037                   dd.lock();
34038               }
34039               inited = true;
34040           }
34041           this.enable(); 
34042        },
34043
34044     /**
34045      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34046      * are supported:
34047      * <pre>
34048 Property    Type                   Description
34049 ----------  ---------------------  ------------------------------------------------------------------------
34050 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34051      * </ul>
34052      * @param {Object} config The config object
34053      */
34054        register : function(config){
34055            var cs = config instanceof Array ? config : arguments;
34056            for(var i = 0, len = cs.length; i < len; i++) {
34057                var c = cs[i];
34058                var target = c.target;
34059                if(target){
34060                    if(target instanceof Array){
34061                        for(var j = 0, jlen = target.length; j < jlen; j++){
34062                            tagEls[target[j]] = c;
34063                        }
34064                    }else{
34065                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34066                    }
34067                }
34068            }
34069        },
34070
34071     /**
34072      * Removes this quick tip from its element and destroys it.
34073      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34074      */
34075        unregister : function(el){
34076            delete tagEls[Roo.id(el)];
34077        },
34078
34079     /**
34080      * Enable this quick tip.
34081      */
34082        enable : function(){
34083            if(inited && disabled){
34084                locks.pop();
34085                if(locks.length < 1){
34086                    disabled = false;
34087                }
34088            }
34089        },
34090
34091     /**
34092      * Disable this quick tip.
34093      */
34094        disable : function(){
34095           disabled = true;
34096           clearTimeout(showProc);
34097           clearTimeout(hideProc);
34098           clearTimeout(dismissProc);
34099           if(ce){
34100               hide(true);
34101           }
34102           locks.push(1);
34103        },
34104
34105     /**
34106      * Returns true if the quick tip is enabled, else false.
34107      */
34108        isEnabled : function(){
34109             return !disabled;
34110        },
34111
34112         // private
34113        tagConfig : {
34114            namespace : "roo", // was ext?? this may break..
34115            alt_namespace : "ext",
34116            attribute : "qtip",
34117            width : "width",
34118            target : "target",
34119            title : "qtitle",
34120            hide : "hide",
34121            cls : "qclass"
34122        }
34123    };
34124 }();
34125
34126 // backwards compat
34127 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34128  * Based on:
34129  * Ext JS Library 1.1.1
34130  * Copyright(c) 2006-2007, Ext JS, LLC.
34131  *
34132  * Originally Released Under LGPL - original licence link has changed is not relivant.
34133  *
34134  * Fork - LGPL
34135  * <script type="text/javascript">
34136  */
34137  
34138
34139 /**
34140  * @class Roo.tree.TreePanel
34141  * @extends Roo.data.Tree
34142
34143  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34144  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34145  * @cfg {Boolean} enableDD true to enable drag and drop
34146  * @cfg {Boolean} enableDrag true to enable just drag
34147  * @cfg {Boolean} enableDrop true to enable just drop
34148  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34149  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34150  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34151  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34152  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34153  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34154  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34155  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34156  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34157  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34158  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34159  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34160  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34161  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34162  * @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>
34163  * @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>
34164  * 
34165  * @constructor
34166  * @param {String/HTMLElement/Element} el The container element
34167  * @param {Object} config
34168  */
34169 Roo.tree.TreePanel = function(el, config){
34170     var root = false;
34171     var loader = false;
34172     if (config.root) {
34173         root = config.root;
34174         delete config.root;
34175     }
34176     if (config.loader) {
34177         loader = config.loader;
34178         delete config.loader;
34179     }
34180     
34181     Roo.apply(this, config);
34182     Roo.tree.TreePanel.superclass.constructor.call(this);
34183     this.el = Roo.get(el);
34184     this.el.addClass('x-tree');
34185     //console.log(root);
34186     if (root) {
34187         this.setRootNode( Roo.factory(root, Roo.tree));
34188     }
34189     if (loader) {
34190         this.loader = Roo.factory(loader, Roo.tree);
34191     }
34192    /**
34193     * Read-only. The id of the container element becomes this TreePanel's id.
34194     */
34195     this.id = this.el.id;
34196     this.addEvents({
34197         /**
34198         * @event beforeload
34199         * Fires before a node is loaded, return false to cancel
34200         * @param {Node} node The node being loaded
34201         */
34202         "beforeload" : true,
34203         /**
34204         * @event load
34205         * Fires when a node is loaded
34206         * @param {Node} node The node that was loaded
34207         */
34208         "load" : true,
34209         /**
34210         * @event textchange
34211         * Fires when the text for a node is changed
34212         * @param {Node} node The node
34213         * @param {String} text The new text
34214         * @param {String} oldText The old text
34215         */
34216         "textchange" : true,
34217         /**
34218         * @event beforeexpand
34219         * Fires before a node is expanded, return false to cancel.
34220         * @param {Node} node The node
34221         * @param {Boolean} deep
34222         * @param {Boolean} anim
34223         */
34224         "beforeexpand" : true,
34225         /**
34226         * @event beforecollapse
34227         * Fires before a node is collapsed, return false to cancel.
34228         * @param {Node} node The node
34229         * @param {Boolean} deep
34230         * @param {Boolean} anim
34231         */
34232         "beforecollapse" : true,
34233         /**
34234         * @event expand
34235         * Fires when a node is expanded
34236         * @param {Node} node The node
34237         */
34238         "expand" : true,
34239         /**
34240         * @event disabledchange
34241         * Fires when the disabled status of a node changes
34242         * @param {Node} node The node
34243         * @param {Boolean} disabled
34244         */
34245         "disabledchange" : true,
34246         /**
34247         * @event collapse
34248         * Fires when a node is collapsed
34249         * @param {Node} node The node
34250         */
34251         "collapse" : true,
34252         /**
34253         * @event beforeclick
34254         * Fires before click processing on a node. Return false to cancel the default action.
34255         * @param {Node} node The node
34256         * @param {Roo.EventObject} e The event object
34257         */
34258         "beforeclick":true,
34259         /**
34260         * @event checkchange
34261         * Fires when a node with a checkbox's checked property changes
34262         * @param {Node} this This node
34263         * @param {Boolean} checked
34264         */
34265         "checkchange":true,
34266         /**
34267         * @event click
34268         * Fires when a node is clicked
34269         * @param {Node} node The node
34270         * @param {Roo.EventObject} e The event object
34271         */
34272         "click":true,
34273         /**
34274         * @event dblclick
34275         * Fires when a node is double clicked
34276         * @param {Node} node The node
34277         * @param {Roo.EventObject} e The event object
34278         */
34279         "dblclick":true,
34280         /**
34281         * @event contextmenu
34282         * Fires when a node is right clicked
34283         * @param {Node} node The node
34284         * @param {Roo.EventObject} e The event object
34285         */
34286         "contextmenu":true,
34287         /**
34288         * @event beforechildrenrendered
34289         * Fires right before the child nodes for a node are rendered
34290         * @param {Node} node The node
34291         */
34292         "beforechildrenrendered":true,
34293         /**
34294         * @event startdrag
34295         * Fires when a node starts being dragged
34296         * @param {Roo.tree.TreePanel} this
34297         * @param {Roo.tree.TreeNode} node
34298         * @param {event} e The raw browser event
34299         */ 
34300        "startdrag" : true,
34301        /**
34302         * @event enddrag
34303         * Fires when a drag operation is complete
34304         * @param {Roo.tree.TreePanel} this
34305         * @param {Roo.tree.TreeNode} node
34306         * @param {event} e The raw browser event
34307         */
34308        "enddrag" : true,
34309        /**
34310         * @event dragdrop
34311         * Fires when a dragged node is dropped on a valid DD target
34312         * @param {Roo.tree.TreePanel} this
34313         * @param {Roo.tree.TreeNode} node
34314         * @param {DD} dd The dd it was dropped on
34315         * @param {event} e The raw browser event
34316         */
34317        "dragdrop" : true,
34318        /**
34319         * @event beforenodedrop
34320         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34321         * passed to handlers has the following properties:<br />
34322         * <ul style="padding:5px;padding-left:16px;">
34323         * <li>tree - The TreePanel</li>
34324         * <li>target - The node being targeted for the drop</li>
34325         * <li>data - The drag data from the drag source</li>
34326         * <li>point - The point of the drop - append, above or below</li>
34327         * <li>source - The drag source</li>
34328         * <li>rawEvent - Raw mouse event</li>
34329         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34330         * to be inserted by setting them on this object.</li>
34331         * <li>cancel - Set this to true to cancel the drop.</li>
34332         * </ul>
34333         * @param {Object} dropEvent
34334         */
34335        "beforenodedrop" : true,
34336        /**
34337         * @event nodedrop
34338         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34339         * passed to handlers has the following properties:<br />
34340         * <ul style="padding:5px;padding-left:16px;">
34341         * <li>tree - The TreePanel</li>
34342         * <li>target - The node being targeted for the drop</li>
34343         * <li>data - The drag data from the drag source</li>
34344         * <li>point - The point of the drop - append, above or below</li>
34345         * <li>source - The drag source</li>
34346         * <li>rawEvent - Raw mouse event</li>
34347         * <li>dropNode - Dropped node(s).</li>
34348         * </ul>
34349         * @param {Object} dropEvent
34350         */
34351        "nodedrop" : true,
34352         /**
34353         * @event nodedragover
34354         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34355         * passed to handlers has the following properties:<br />
34356         * <ul style="padding:5px;padding-left:16px;">
34357         * <li>tree - The TreePanel</li>
34358         * <li>target - The node being targeted for the drop</li>
34359         * <li>data - The drag data from the drag source</li>
34360         * <li>point - The point of the drop - append, above or below</li>
34361         * <li>source - The drag source</li>
34362         * <li>rawEvent - Raw mouse event</li>
34363         * <li>dropNode - Drop node(s) provided by the source.</li>
34364         * <li>cancel - Set this to true to signal drop not allowed.</li>
34365         * </ul>
34366         * @param {Object} dragOverEvent
34367         */
34368        "nodedragover" : true,
34369        /**
34370         * @event appendnode
34371         * Fires when append node to the tree
34372         * @param {Roo.tree.TreePanel} this
34373         * @param {Roo.tree.TreeNode} node
34374         * @param {Number} index The index of the newly appended node
34375         */
34376        "appendnode" : true
34377         
34378     });
34379     if(this.singleExpand){
34380        this.on("beforeexpand", this.restrictExpand, this);
34381     }
34382     if (this.editor) {
34383         this.editor.tree = this;
34384         this.editor = Roo.factory(this.editor, Roo.tree);
34385     }
34386     
34387     if (this.selModel) {
34388         this.selModel = Roo.factory(this.selModel, Roo.tree);
34389     }
34390    
34391 };
34392 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34393     rootVisible : true,
34394     animate: Roo.enableFx,
34395     lines : true,
34396     enableDD : false,
34397     hlDrop : Roo.enableFx,
34398   
34399     renderer: false,
34400     
34401     rendererTip: false,
34402     // private
34403     restrictExpand : function(node){
34404         var p = node.parentNode;
34405         if(p){
34406             if(p.expandedChild && p.expandedChild.parentNode == p){
34407                 p.expandedChild.collapse();
34408             }
34409             p.expandedChild = node;
34410         }
34411     },
34412
34413     // private override
34414     setRootNode : function(node){
34415         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34416         if(!this.rootVisible){
34417             node.ui = new Roo.tree.RootTreeNodeUI(node);
34418         }
34419         return node;
34420     },
34421
34422     /**
34423      * Returns the container element for this TreePanel
34424      */
34425     getEl : function(){
34426         return this.el;
34427     },
34428
34429     /**
34430      * Returns the default TreeLoader for this TreePanel
34431      */
34432     getLoader : function(){
34433         return this.loader;
34434     },
34435
34436     /**
34437      * Expand all nodes
34438      */
34439     expandAll : function(){
34440         this.root.expand(true);
34441     },
34442
34443     /**
34444      * Collapse all nodes
34445      */
34446     collapseAll : function(){
34447         this.root.collapse(true);
34448     },
34449
34450     /**
34451      * Returns the selection model used by this TreePanel
34452      */
34453     getSelectionModel : function(){
34454         if(!this.selModel){
34455             this.selModel = new Roo.tree.DefaultSelectionModel();
34456         }
34457         return this.selModel;
34458     },
34459
34460     /**
34461      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34462      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34463      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34464      * @return {Array}
34465      */
34466     getChecked : function(a, startNode){
34467         startNode = startNode || this.root;
34468         var r = [];
34469         var f = function(){
34470             if(this.attributes.checked){
34471                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34472             }
34473         }
34474         startNode.cascade(f);
34475         return r;
34476     },
34477
34478     /**
34479      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34480      * @param {String} path
34481      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34482      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34483      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34484      */
34485     expandPath : function(path, attr, callback){
34486         attr = attr || "id";
34487         var keys = path.split(this.pathSeparator);
34488         var curNode = this.root;
34489         if(curNode.attributes[attr] != keys[1]){ // invalid root
34490             if(callback){
34491                 callback(false, null);
34492             }
34493             return;
34494         }
34495         var index = 1;
34496         var f = function(){
34497             if(++index == keys.length){
34498                 if(callback){
34499                     callback(true, curNode);
34500                 }
34501                 return;
34502             }
34503             var c = curNode.findChild(attr, keys[index]);
34504             if(!c){
34505                 if(callback){
34506                     callback(false, curNode);
34507                 }
34508                 return;
34509             }
34510             curNode = c;
34511             c.expand(false, false, f);
34512         };
34513         curNode.expand(false, false, f);
34514     },
34515
34516     /**
34517      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34518      * @param {String} path
34519      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34520      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34521      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34522      */
34523     selectPath : function(path, attr, callback){
34524         attr = attr || "id";
34525         var keys = path.split(this.pathSeparator);
34526         var v = keys.pop();
34527         if(keys.length > 0){
34528             var f = function(success, node){
34529                 if(success && node){
34530                     var n = node.findChild(attr, v);
34531                     if(n){
34532                         n.select();
34533                         if(callback){
34534                             callback(true, n);
34535                         }
34536                     }else if(callback){
34537                         callback(false, n);
34538                     }
34539                 }else{
34540                     if(callback){
34541                         callback(false, n);
34542                     }
34543                 }
34544             };
34545             this.expandPath(keys.join(this.pathSeparator), attr, f);
34546         }else{
34547             this.root.select();
34548             if(callback){
34549                 callback(true, this.root);
34550             }
34551         }
34552     },
34553
34554     getTreeEl : function(){
34555         return this.el;
34556     },
34557
34558     /**
34559      * Trigger rendering of this TreePanel
34560      */
34561     render : function(){
34562         if (this.innerCt) {
34563             return this; // stop it rendering more than once!!
34564         }
34565         
34566         this.innerCt = this.el.createChild({tag:"ul",
34567                cls:"x-tree-root-ct " +
34568                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34569
34570         if(this.containerScroll){
34571             Roo.dd.ScrollManager.register(this.el);
34572         }
34573         if((this.enableDD || this.enableDrop) && !this.dropZone){
34574            /**
34575             * The dropZone used by this tree if drop is enabled
34576             * @type Roo.tree.TreeDropZone
34577             */
34578              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34579                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34580            });
34581         }
34582         if((this.enableDD || this.enableDrag) && !this.dragZone){
34583            /**
34584             * The dragZone used by this tree if drag is enabled
34585             * @type Roo.tree.TreeDragZone
34586             */
34587             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34588                ddGroup: this.ddGroup || "TreeDD",
34589                scroll: this.ddScroll
34590            });
34591         }
34592         this.getSelectionModel().init(this);
34593         if (!this.root) {
34594             Roo.log("ROOT not set in tree");
34595             return this;
34596         }
34597         this.root.render();
34598         if(!this.rootVisible){
34599             this.root.renderChildren();
34600         }
34601         return this;
34602     }
34603 });/*
34604  * Based on:
34605  * Ext JS Library 1.1.1
34606  * Copyright(c) 2006-2007, Ext JS, LLC.
34607  *
34608  * Originally Released Under LGPL - original licence link has changed is not relivant.
34609  *
34610  * Fork - LGPL
34611  * <script type="text/javascript">
34612  */
34613  
34614
34615 /**
34616  * @class Roo.tree.DefaultSelectionModel
34617  * @extends Roo.util.Observable
34618  * The default single selection for a TreePanel.
34619  * @param {Object} cfg Configuration
34620  */
34621 Roo.tree.DefaultSelectionModel = function(cfg){
34622    this.selNode = null;
34623    
34624    
34625    
34626    this.addEvents({
34627        /**
34628         * @event selectionchange
34629         * Fires when the selected node changes
34630         * @param {DefaultSelectionModel} this
34631         * @param {TreeNode} node the new selection
34632         */
34633        "selectionchange" : true,
34634
34635        /**
34636         * @event beforeselect
34637         * Fires before the selected node changes, return false to cancel the change
34638         * @param {DefaultSelectionModel} this
34639         * @param {TreeNode} node the new selection
34640         * @param {TreeNode} node the old selection
34641         */
34642        "beforeselect" : true
34643    });
34644    
34645     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34646 };
34647
34648 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34649     init : function(tree){
34650         this.tree = tree;
34651         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34652         tree.on("click", this.onNodeClick, this);
34653     },
34654     
34655     onNodeClick : function(node, e){
34656         if (e.ctrlKey && this.selNode == node)  {
34657             this.unselect(node);
34658             return;
34659         }
34660         this.select(node);
34661     },
34662     
34663     /**
34664      * Select a node.
34665      * @param {TreeNode} node The node to select
34666      * @return {TreeNode} The selected node
34667      */
34668     select : function(node){
34669         var last = this.selNode;
34670         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34671             if(last){
34672                 last.ui.onSelectedChange(false);
34673             }
34674             this.selNode = node;
34675             node.ui.onSelectedChange(true);
34676             this.fireEvent("selectionchange", this, node, last);
34677         }
34678         return node;
34679     },
34680     
34681     /**
34682      * Deselect a node.
34683      * @param {TreeNode} node The node to unselect
34684      */
34685     unselect : function(node){
34686         if(this.selNode == node){
34687             this.clearSelections();
34688         }    
34689     },
34690     
34691     /**
34692      * Clear all selections
34693      */
34694     clearSelections : function(){
34695         var n = this.selNode;
34696         if(n){
34697             n.ui.onSelectedChange(false);
34698             this.selNode = null;
34699             this.fireEvent("selectionchange", this, null);
34700         }
34701         return n;
34702     },
34703     
34704     /**
34705      * Get the selected node
34706      * @return {TreeNode} The selected node
34707      */
34708     getSelectedNode : function(){
34709         return this.selNode;    
34710     },
34711     
34712     /**
34713      * Returns true if the node is selected
34714      * @param {TreeNode} node The node to check
34715      * @return {Boolean}
34716      */
34717     isSelected : function(node){
34718         return this.selNode == node;  
34719     },
34720
34721     /**
34722      * Selects the node above the selected node in the tree, intelligently walking the nodes
34723      * @return TreeNode The new selection
34724      */
34725     selectPrevious : function(){
34726         var s = this.selNode || this.lastSelNode;
34727         if(!s){
34728             return null;
34729         }
34730         var ps = s.previousSibling;
34731         if(ps){
34732             if(!ps.isExpanded() || ps.childNodes.length < 1){
34733                 return this.select(ps);
34734             } else{
34735                 var lc = ps.lastChild;
34736                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34737                     lc = lc.lastChild;
34738                 }
34739                 return this.select(lc);
34740             }
34741         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34742             return this.select(s.parentNode);
34743         }
34744         return null;
34745     },
34746
34747     /**
34748      * Selects the node above the selected node in the tree, intelligently walking the nodes
34749      * @return TreeNode The new selection
34750      */
34751     selectNext : function(){
34752         var s = this.selNode || this.lastSelNode;
34753         if(!s){
34754             return null;
34755         }
34756         if(s.firstChild && s.isExpanded()){
34757              return this.select(s.firstChild);
34758          }else if(s.nextSibling){
34759              return this.select(s.nextSibling);
34760          }else if(s.parentNode){
34761             var newS = null;
34762             s.parentNode.bubble(function(){
34763                 if(this.nextSibling){
34764                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34765                     return false;
34766                 }
34767             });
34768             return newS;
34769          }
34770         return null;
34771     },
34772
34773     onKeyDown : function(e){
34774         var s = this.selNode || this.lastSelNode;
34775         // undesirable, but required
34776         var sm = this;
34777         if(!s){
34778             return;
34779         }
34780         var k = e.getKey();
34781         switch(k){
34782              case e.DOWN:
34783                  e.stopEvent();
34784                  this.selectNext();
34785              break;
34786              case e.UP:
34787                  e.stopEvent();
34788                  this.selectPrevious();
34789              break;
34790              case e.RIGHT:
34791                  e.preventDefault();
34792                  if(s.hasChildNodes()){
34793                      if(!s.isExpanded()){
34794                          s.expand();
34795                      }else if(s.firstChild){
34796                          this.select(s.firstChild, e);
34797                      }
34798                  }
34799              break;
34800              case e.LEFT:
34801                  e.preventDefault();
34802                  if(s.hasChildNodes() && s.isExpanded()){
34803                      s.collapse();
34804                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34805                      this.select(s.parentNode, e);
34806                  }
34807              break;
34808         };
34809     }
34810 });
34811
34812 /**
34813  * @class Roo.tree.MultiSelectionModel
34814  * @extends Roo.util.Observable
34815  * Multi selection for a TreePanel.
34816  * @param {Object} cfg Configuration
34817  */
34818 Roo.tree.MultiSelectionModel = function(){
34819    this.selNodes = [];
34820    this.selMap = {};
34821    this.addEvents({
34822        /**
34823         * @event selectionchange
34824         * Fires when the selected nodes change
34825         * @param {MultiSelectionModel} this
34826         * @param {Array} nodes Array of the selected nodes
34827         */
34828        "selectionchange" : true
34829    });
34830    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34831    
34832 };
34833
34834 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34835     init : function(tree){
34836         this.tree = tree;
34837         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34838         tree.on("click", this.onNodeClick, this);
34839     },
34840     
34841     onNodeClick : function(node, e){
34842         this.select(node, e, e.ctrlKey);
34843     },
34844     
34845     /**
34846      * Select a node.
34847      * @param {TreeNode} node The node to select
34848      * @param {EventObject} e (optional) An event associated with the selection
34849      * @param {Boolean} keepExisting True to retain existing selections
34850      * @return {TreeNode} The selected node
34851      */
34852     select : function(node, e, keepExisting){
34853         if(keepExisting !== true){
34854             this.clearSelections(true);
34855         }
34856         if(this.isSelected(node)){
34857             this.lastSelNode = node;
34858             return node;
34859         }
34860         this.selNodes.push(node);
34861         this.selMap[node.id] = node;
34862         this.lastSelNode = node;
34863         node.ui.onSelectedChange(true);
34864         this.fireEvent("selectionchange", this, this.selNodes);
34865         return node;
34866     },
34867     
34868     /**
34869      * Deselect a node.
34870      * @param {TreeNode} node The node to unselect
34871      */
34872     unselect : function(node){
34873         if(this.selMap[node.id]){
34874             node.ui.onSelectedChange(false);
34875             var sn = this.selNodes;
34876             var index = -1;
34877             if(sn.indexOf){
34878                 index = sn.indexOf(node);
34879             }else{
34880                 for(var i = 0, len = sn.length; i < len; i++){
34881                     if(sn[i] == node){
34882                         index = i;
34883                         break;
34884                     }
34885                 }
34886             }
34887             if(index != -1){
34888                 this.selNodes.splice(index, 1);
34889             }
34890             delete this.selMap[node.id];
34891             this.fireEvent("selectionchange", this, this.selNodes);
34892         }
34893     },
34894     
34895     /**
34896      * Clear all selections
34897      */
34898     clearSelections : function(suppressEvent){
34899         var sn = this.selNodes;
34900         if(sn.length > 0){
34901             for(var i = 0, len = sn.length; i < len; i++){
34902                 sn[i].ui.onSelectedChange(false);
34903             }
34904             this.selNodes = [];
34905             this.selMap = {};
34906             if(suppressEvent !== true){
34907                 this.fireEvent("selectionchange", this, this.selNodes);
34908             }
34909         }
34910     },
34911     
34912     /**
34913      * Returns true if the node is selected
34914      * @param {TreeNode} node The node to check
34915      * @return {Boolean}
34916      */
34917     isSelected : function(node){
34918         return this.selMap[node.id] ? true : false;  
34919     },
34920     
34921     /**
34922      * Returns an array of the selected nodes
34923      * @return {Array}
34924      */
34925     getSelectedNodes : function(){
34926         return this.selNodes;    
34927     },
34928
34929     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34930
34931     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34932
34933     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34934 });/*
34935  * Based on:
34936  * Ext JS Library 1.1.1
34937  * Copyright(c) 2006-2007, Ext JS, LLC.
34938  *
34939  * Originally Released Under LGPL - original licence link has changed is not relivant.
34940  *
34941  * Fork - LGPL
34942  * <script type="text/javascript">
34943  */
34944  
34945 /**
34946  * @class Roo.tree.TreeNode
34947  * @extends Roo.data.Node
34948  * @cfg {String} text The text for this node
34949  * @cfg {Boolean} expanded true to start the node expanded
34950  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34951  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34952  * @cfg {Boolean} disabled true to start the node disabled
34953  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34954  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34955  * @cfg {String} cls A css class to be added to the node
34956  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34957  * @cfg {String} href URL of the link used for the node (defaults to #)
34958  * @cfg {String} hrefTarget target frame for the link
34959  * @cfg {String} qtip An Ext QuickTip for the node
34960  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34961  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34962  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34963  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34964  * (defaults to undefined with no checkbox rendered)
34965  * @constructor
34966  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34967  */
34968 Roo.tree.TreeNode = function(attributes){
34969     attributes = attributes || {};
34970     if(typeof attributes == "string"){
34971         attributes = {text: attributes};
34972     }
34973     this.childrenRendered = false;
34974     this.rendered = false;
34975     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34976     this.expanded = attributes.expanded === true;
34977     this.isTarget = attributes.isTarget !== false;
34978     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34979     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34980
34981     /**
34982      * Read-only. The text for this node. To change it use setText().
34983      * @type String
34984      */
34985     this.text = attributes.text;
34986     /**
34987      * True if this node is disabled.
34988      * @type Boolean
34989      */
34990     this.disabled = attributes.disabled === true;
34991
34992     this.addEvents({
34993         /**
34994         * @event textchange
34995         * Fires when the text for this node is changed
34996         * @param {Node} this This node
34997         * @param {String} text The new text
34998         * @param {String} oldText The old text
34999         */
35000         "textchange" : true,
35001         /**
35002         * @event beforeexpand
35003         * Fires before this node is expanded, return false to cancel.
35004         * @param {Node} this This node
35005         * @param {Boolean} deep
35006         * @param {Boolean} anim
35007         */
35008         "beforeexpand" : true,
35009         /**
35010         * @event beforecollapse
35011         * Fires before this node is collapsed, return false to cancel.
35012         * @param {Node} this This node
35013         * @param {Boolean} deep
35014         * @param {Boolean} anim
35015         */
35016         "beforecollapse" : true,
35017         /**
35018         * @event expand
35019         * Fires when this node is expanded
35020         * @param {Node} this This node
35021         */
35022         "expand" : true,
35023         /**
35024         * @event disabledchange
35025         * Fires when the disabled status of this node changes
35026         * @param {Node} this This node
35027         * @param {Boolean} disabled
35028         */
35029         "disabledchange" : true,
35030         /**
35031         * @event collapse
35032         * Fires when this node is collapsed
35033         * @param {Node} this This node
35034         */
35035         "collapse" : true,
35036         /**
35037         * @event beforeclick
35038         * Fires before click processing. Return false to cancel the default action.
35039         * @param {Node} this This node
35040         * @param {Roo.EventObject} e The event object
35041         */
35042         "beforeclick":true,
35043         /**
35044         * @event checkchange
35045         * Fires when a node with a checkbox's checked property changes
35046         * @param {Node} this This node
35047         * @param {Boolean} checked
35048         */
35049         "checkchange":true,
35050         /**
35051         * @event click
35052         * Fires when this node is clicked
35053         * @param {Node} this This node
35054         * @param {Roo.EventObject} e The event object
35055         */
35056         "click":true,
35057         /**
35058         * @event dblclick
35059         * Fires when this node is double clicked
35060         * @param {Node} this This node
35061         * @param {Roo.EventObject} e The event object
35062         */
35063         "dblclick":true,
35064         /**
35065         * @event contextmenu
35066         * Fires when this node is right clicked
35067         * @param {Node} this This node
35068         * @param {Roo.EventObject} e The event object
35069         */
35070         "contextmenu":true,
35071         /**
35072         * @event beforechildrenrendered
35073         * Fires right before the child nodes for this node are rendered
35074         * @param {Node} this This node
35075         */
35076         "beforechildrenrendered":true
35077     });
35078
35079     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35080
35081     /**
35082      * Read-only. The UI for this node
35083      * @type TreeNodeUI
35084      */
35085     this.ui = new uiClass(this);
35086     
35087     // finally support items[]
35088     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35089         return;
35090     }
35091     
35092     
35093     Roo.each(this.attributes.items, function(c) {
35094         this.appendChild(Roo.factory(c,Roo.Tree));
35095     }, this);
35096     delete this.attributes.items;
35097     
35098     
35099     
35100 };
35101 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35102     preventHScroll: true,
35103     /**
35104      * Returns true if this node is expanded
35105      * @return {Boolean}
35106      */
35107     isExpanded : function(){
35108         return this.expanded;
35109     },
35110
35111     /**
35112      * Returns the UI object for this node
35113      * @return {TreeNodeUI}
35114      */
35115     getUI : function(){
35116         return this.ui;
35117     },
35118
35119     // private override
35120     setFirstChild : function(node){
35121         var of = this.firstChild;
35122         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35123         if(this.childrenRendered && of && node != of){
35124             of.renderIndent(true, true);
35125         }
35126         if(this.rendered){
35127             this.renderIndent(true, true);
35128         }
35129     },
35130
35131     // private override
35132     setLastChild : function(node){
35133         var ol = this.lastChild;
35134         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35135         if(this.childrenRendered && ol && node != ol){
35136             ol.renderIndent(true, true);
35137         }
35138         if(this.rendered){
35139             this.renderIndent(true, true);
35140         }
35141     },
35142
35143     // these methods are overridden to provide lazy rendering support
35144     // private override
35145     appendChild : function()
35146     {
35147         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35148         if(node && this.childrenRendered){
35149             node.render();
35150         }
35151         this.ui.updateExpandIcon();
35152         return node;
35153     },
35154
35155     // private override
35156     removeChild : function(node){
35157         this.ownerTree.getSelectionModel().unselect(node);
35158         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35159         // if it's been rendered remove dom node
35160         if(this.childrenRendered){
35161             node.ui.remove();
35162         }
35163         if(this.childNodes.length < 1){
35164             this.collapse(false, false);
35165         }else{
35166             this.ui.updateExpandIcon();
35167         }
35168         if(!this.firstChild) {
35169             this.childrenRendered = false;
35170         }
35171         return node;
35172     },
35173
35174     // private override
35175     insertBefore : function(node, refNode){
35176         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35177         if(newNode && refNode && this.childrenRendered){
35178             node.render();
35179         }
35180         this.ui.updateExpandIcon();
35181         return newNode;
35182     },
35183
35184     /**
35185      * Sets the text for this node
35186      * @param {String} text
35187      */
35188     setText : function(text){
35189         var oldText = this.text;
35190         this.text = text;
35191         this.attributes.text = text;
35192         if(this.rendered){ // event without subscribing
35193             this.ui.onTextChange(this, text, oldText);
35194         }
35195         this.fireEvent("textchange", this, text, oldText);
35196     },
35197
35198     /**
35199      * Triggers selection of this node
35200      */
35201     select : function(){
35202         this.getOwnerTree().getSelectionModel().select(this);
35203     },
35204
35205     /**
35206      * Triggers deselection of this node
35207      */
35208     unselect : function(){
35209         this.getOwnerTree().getSelectionModel().unselect(this);
35210     },
35211
35212     /**
35213      * Returns true if this node is selected
35214      * @return {Boolean}
35215      */
35216     isSelected : function(){
35217         return this.getOwnerTree().getSelectionModel().isSelected(this);
35218     },
35219
35220     /**
35221      * Expand this node.
35222      * @param {Boolean} deep (optional) True to expand all children as well
35223      * @param {Boolean} anim (optional) false to cancel the default animation
35224      * @param {Function} callback (optional) A callback to be called when
35225      * expanding this node completes (does not wait for deep expand to complete).
35226      * Called with 1 parameter, this node.
35227      */
35228     expand : function(deep, anim, callback){
35229         if(!this.expanded){
35230             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35231                 return;
35232             }
35233             if(!this.childrenRendered){
35234                 this.renderChildren();
35235             }
35236             this.expanded = true;
35237             
35238             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35239                 this.ui.animExpand(function(){
35240                     this.fireEvent("expand", this);
35241                     if(typeof callback == "function"){
35242                         callback(this);
35243                     }
35244                     if(deep === true){
35245                         this.expandChildNodes(true);
35246                     }
35247                 }.createDelegate(this));
35248                 return;
35249             }else{
35250                 this.ui.expand();
35251                 this.fireEvent("expand", this);
35252                 if(typeof callback == "function"){
35253                     callback(this);
35254                 }
35255             }
35256         }else{
35257            if(typeof callback == "function"){
35258                callback(this);
35259            }
35260         }
35261         if(deep === true){
35262             this.expandChildNodes(true);
35263         }
35264     },
35265
35266     isHiddenRoot : function(){
35267         return this.isRoot && !this.getOwnerTree().rootVisible;
35268     },
35269
35270     /**
35271      * Collapse this node.
35272      * @param {Boolean} deep (optional) True to collapse all children as well
35273      * @param {Boolean} anim (optional) false to cancel the default animation
35274      */
35275     collapse : function(deep, anim){
35276         if(this.expanded && !this.isHiddenRoot()){
35277             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35278                 return;
35279             }
35280             this.expanded = false;
35281             if((this.getOwnerTree().animate && anim !== false) || anim){
35282                 this.ui.animCollapse(function(){
35283                     this.fireEvent("collapse", this);
35284                     if(deep === true){
35285                         this.collapseChildNodes(true);
35286                     }
35287                 }.createDelegate(this));
35288                 return;
35289             }else{
35290                 this.ui.collapse();
35291                 this.fireEvent("collapse", this);
35292             }
35293         }
35294         if(deep === true){
35295             var cs = this.childNodes;
35296             for(var i = 0, len = cs.length; i < len; i++) {
35297                 cs[i].collapse(true, false);
35298             }
35299         }
35300     },
35301
35302     // private
35303     delayedExpand : function(delay){
35304         if(!this.expandProcId){
35305             this.expandProcId = this.expand.defer(delay, this);
35306         }
35307     },
35308
35309     // private
35310     cancelExpand : function(){
35311         if(this.expandProcId){
35312             clearTimeout(this.expandProcId);
35313         }
35314         this.expandProcId = false;
35315     },
35316
35317     /**
35318      * Toggles expanded/collapsed state of the node
35319      */
35320     toggle : function(){
35321         if(this.expanded){
35322             this.collapse();
35323         }else{
35324             this.expand();
35325         }
35326     },
35327
35328     /**
35329      * Ensures all parent nodes are expanded
35330      */
35331     ensureVisible : function(callback){
35332         var tree = this.getOwnerTree();
35333         tree.expandPath(this.parentNode.getPath(), false, function(){
35334             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35335             Roo.callback(callback);
35336         }.createDelegate(this));
35337     },
35338
35339     /**
35340      * Expand all child nodes
35341      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35342      */
35343     expandChildNodes : function(deep){
35344         var cs = this.childNodes;
35345         for(var i = 0, len = cs.length; i < len; i++) {
35346                 cs[i].expand(deep);
35347         }
35348     },
35349
35350     /**
35351      * Collapse all child nodes
35352      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35353      */
35354     collapseChildNodes : function(deep){
35355         var cs = this.childNodes;
35356         for(var i = 0, len = cs.length; i < len; i++) {
35357                 cs[i].collapse(deep);
35358         }
35359     },
35360
35361     /**
35362      * Disables this node
35363      */
35364     disable : function(){
35365         this.disabled = true;
35366         this.unselect();
35367         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35368             this.ui.onDisableChange(this, true);
35369         }
35370         this.fireEvent("disabledchange", this, true);
35371     },
35372
35373     /**
35374      * Enables this node
35375      */
35376     enable : function(){
35377         this.disabled = false;
35378         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35379             this.ui.onDisableChange(this, false);
35380         }
35381         this.fireEvent("disabledchange", this, false);
35382     },
35383
35384     // private
35385     renderChildren : function(suppressEvent){
35386         if(suppressEvent !== false){
35387             this.fireEvent("beforechildrenrendered", this);
35388         }
35389         var cs = this.childNodes;
35390         for(var i = 0, len = cs.length; i < len; i++){
35391             cs[i].render(true);
35392         }
35393         this.childrenRendered = true;
35394     },
35395
35396     // private
35397     sort : function(fn, scope){
35398         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35399         if(this.childrenRendered){
35400             var cs = this.childNodes;
35401             for(var i = 0, len = cs.length; i < len; i++){
35402                 cs[i].render(true);
35403             }
35404         }
35405     },
35406
35407     // private
35408     render : function(bulkRender){
35409         this.ui.render(bulkRender);
35410         if(!this.rendered){
35411             this.rendered = true;
35412             if(this.expanded){
35413                 this.expanded = false;
35414                 this.expand(false, false);
35415             }
35416         }
35417     },
35418
35419     // private
35420     renderIndent : function(deep, refresh){
35421         if(refresh){
35422             this.ui.childIndent = null;
35423         }
35424         this.ui.renderIndent();
35425         if(deep === true && this.childrenRendered){
35426             var cs = this.childNodes;
35427             for(var i = 0, len = cs.length; i < len; i++){
35428                 cs[i].renderIndent(true, refresh);
35429             }
35430         }
35431     }
35432 });/*
35433  * Based on:
35434  * Ext JS Library 1.1.1
35435  * Copyright(c) 2006-2007, Ext JS, LLC.
35436  *
35437  * Originally Released Under LGPL - original licence link has changed is not relivant.
35438  *
35439  * Fork - LGPL
35440  * <script type="text/javascript">
35441  */
35442  
35443 /**
35444  * @class Roo.tree.AsyncTreeNode
35445  * @extends Roo.tree.TreeNode
35446  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35447  * @constructor
35448  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35449  */
35450  Roo.tree.AsyncTreeNode = function(config){
35451     this.loaded = false;
35452     this.loading = false;
35453     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35454     /**
35455     * @event beforeload
35456     * Fires before this node is loaded, return false to cancel
35457     * @param {Node} this This node
35458     */
35459     this.addEvents({'beforeload':true, 'load': true});
35460     /**
35461     * @event load
35462     * Fires when this node is loaded
35463     * @param {Node} this This node
35464     */
35465     /**
35466      * The loader used by this node (defaults to using the tree's defined loader)
35467      * @type TreeLoader
35468      * @property loader
35469      */
35470 };
35471 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35472     expand : function(deep, anim, callback){
35473         if(this.loading){ // if an async load is already running, waiting til it's done
35474             var timer;
35475             var f = function(){
35476                 if(!this.loading){ // done loading
35477                     clearInterval(timer);
35478                     this.expand(deep, anim, callback);
35479                 }
35480             }.createDelegate(this);
35481             timer = setInterval(f, 200);
35482             return;
35483         }
35484         if(!this.loaded){
35485             if(this.fireEvent("beforeload", this) === false){
35486                 return;
35487             }
35488             this.loading = true;
35489             this.ui.beforeLoad(this);
35490             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35491             if(loader){
35492                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35493                 return;
35494             }
35495         }
35496         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35497     },
35498     
35499     /**
35500      * Returns true if this node is currently loading
35501      * @return {Boolean}
35502      */
35503     isLoading : function(){
35504         return this.loading;  
35505     },
35506     
35507     loadComplete : function(deep, anim, callback){
35508         this.loading = false;
35509         this.loaded = true;
35510         this.ui.afterLoad(this);
35511         this.fireEvent("load", this);
35512         this.expand(deep, anim, callback);
35513     },
35514     
35515     /**
35516      * Returns true if this node has been loaded
35517      * @return {Boolean}
35518      */
35519     isLoaded : function(){
35520         return this.loaded;
35521     },
35522     
35523     hasChildNodes : function(){
35524         if(!this.isLeaf() && !this.loaded){
35525             return true;
35526         }else{
35527             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35528         }
35529     },
35530
35531     /**
35532      * Trigger a reload for this node
35533      * @param {Function} callback
35534      */
35535     reload : function(callback){
35536         this.collapse(false, false);
35537         while(this.firstChild){
35538             this.removeChild(this.firstChild);
35539         }
35540         this.childrenRendered = false;
35541         this.loaded = false;
35542         if(this.isHiddenRoot()){
35543             this.expanded = false;
35544         }
35545         this.expand(false, false, callback);
35546     }
35547 });/*
35548  * Based on:
35549  * Ext JS Library 1.1.1
35550  * Copyright(c) 2006-2007, Ext JS, LLC.
35551  *
35552  * Originally Released Under LGPL - original licence link has changed is not relivant.
35553  *
35554  * Fork - LGPL
35555  * <script type="text/javascript">
35556  */
35557  
35558 /**
35559  * @class Roo.tree.TreeNodeUI
35560  * @constructor
35561  * @param {Object} node The node to render
35562  * The TreeNode UI implementation is separate from the
35563  * tree implementation. Unless you are customizing the tree UI,
35564  * you should never have to use this directly.
35565  */
35566 Roo.tree.TreeNodeUI = function(node){
35567     this.node = node;
35568     this.rendered = false;
35569     this.animating = false;
35570     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35571 };
35572
35573 Roo.tree.TreeNodeUI.prototype = {
35574     removeChild : function(node){
35575         if(this.rendered){
35576             this.ctNode.removeChild(node.ui.getEl());
35577         }
35578     },
35579
35580     beforeLoad : function(){
35581          this.addClass("x-tree-node-loading");
35582     },
35583
35584     afterLoad : function(){
35585          this.removeClass("x-tree-node-loading");
35586     },
35587
35588     onTextChange : function(node, text, oldText){
35589         if(this.rendered){
35590             this.textNode.innerHTML = text;
35591         }
35592     },
35593
35594     onDisableChange : function(node, state){
35595         this.disabled = state;
35596         if(state){
35597             this.addClass("x-tree-node-disabled");
35598         }else{
35599             this.removeClass("x-tree-node-disabled");
35600         }
35601     },
35602
35603     onSelectedChange : function(state){
35604         if(state){
35605             this.focus();
35606             this.addClass("x-tree-selected");
35607         }else{
35608             //this.blur();
35609             this.removeClass("x-tree-selected");
35610         }
35611     },
35612
35613     onMove : function(tree, node, oldParent, newParent, index, refNode){
35614         this.childIndent = null;
35615         if(this.rendered){
35616             var targetNode = newParent.ui.getContainer();
35617             if(!targetNode){//target not rendered
35618                 this.holder = document.createElement("div");
35619                 this.holder.appendChild(this.wrap);
35620                 return;
35621             }
35622             var insertBefore = refNode ? refNode.ui.getEl() : null;
35623             if(insertBefore){
35624                 targetNode.insertBefore(this.wrap, insertBefore);
35625             }else{
35626                 targetNode.appendChild(this.wrap);
35627             }
35628             this.node.renderIndent(true);
35629         }
35630     },
35631
35632     addClass : function(cls){
35633         if(this.elNode){
35634             Roo.fly(this.elNode).addClass(cls);
35635         }
35636     },
35637
35638     removeClass : function(cls){
35639         if(this.elNode){
35640             Roo.fly(this.elNode).removeClass(cls);
35641         }
35642     },
35643
35644     remove : function(){
35645         if(this.rendered){
35646             this.holder = document.createElement("div");
35647             this.holder.appendChild(this.wrap);
35648         }
35649     },
35650
35651     fireEvent : function(){
35652         return this.node.fireEvent.apply(this.node, arguments);
35653     },
35654
35655     initEvents : function(){
35656         this.node.on("move", this.onMove, this);
35657         var E = Roo.EventManager;
35658         var a = this.anchor;
35659
35660         var el = Roo.fly(a, '_treeui');
35661
35662         if(Roo.isOpera){ // opera render bug ignores the CSS
35663             el.setStyle("text-decoration", "none");
35664         }
35665
35666         el.on("click", this.onClick, this);
35667         el.on("dblclick", this.onDblClick, this);
35668
35669         if(this.checkbox){
35670             Roo.EventManager.on(this.checkbox,
35671                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35672         }
35673
35674         el.on("contextmenu", this.onContextMenu, this);
35675
35676         var icon = Roo.fly(this.iconNode);
35677         icon.on("click", this.onClick, this);
35678         icon.on("dblclick", this.onDblClick, this);
35679         icon.on("contextmenu", this.onContextMenu, this);
35680         E.on(this.ecNode, "click", this.ecClick, this, true);
35681
35682         if(this.node.disabled){
35683             this.addClass("x-tree-node-disabled");
35684         }
35685         if(this.node.hidden){
35686             this.addClass("x-tree-node-disabled");
35687         }
35688         var ot = this.node.getOwnerTree();
35689         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35690         if(dd && (!this.node.isRoot || ot.rootVisible)){
35691             Roo.dd.Registry.register(this.elNode, {
35692                 node: this.node,
35693                 handles: this.getDDHandles(),
35694                 isHandle: false
35695             });
35696         }
35697     },
35698
35699     getDDHandles : function(){
35700         return [this.iconNode, this.textNode];
35701     },
35702
35703     hide : function(){
35704         if(this.rendered){
35705             this.wrap.style.display = "none";
35706         }
35707     },
35708
35709     show : function(){
35710         if(this.rendered){
35711             this.wrap.style.display = "";
35712         }
35713     },
35714
35715     onContextMenu : function(e){
35716         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35717             e.preventDefault();
35718             this.focus();
35719             this.fireEvent("contextmenu", this.node, e);
35720         }
35721     },
35722
35723     onClick : function(e){
35724         if(this.dropping){
35725             e.stopEvent();
35726             return;
35727         }
35728         if(this.fireEvent("beforeclick", this.node, e) !== false){
35729             if(!this.disabled && this.node.attributes.href){
35730                 this.fireEvent("click", this.node, e);
35731                 return;
35732             }
35733             e.preventDefault();
35734             if(this.disabled){
35735                 return;
35736             }
35737
35738             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35739                 this.node.toggle();
35740             }
35741
35742             this.fireEvent("click", this.node, e);
35743         }else{
35744             e.stopEvent();
35745         }
35746     },
35747
35748     onDblClick : function(e){
35749         e.preventDefault();
35750         if(this.disabled){
35751             return;
35752         }
35753         if(this.checkbox){
35754             this.toggleCheck();
35755         }
35756         if(!this.animating && this.node.hasChildNodes()){
35757             this.node.toggle();
35758         }
35759         this.fireEvent("dblclick", this.node, e);
35760     },
35761
35762     onCheckChange : function(){
35763         var checked = this.checkbox.checked;
35764         this.node.attributes.checked = checked;
35765         this.fireEvent('checkchange', this.node, checked);
35766     },
35767
35768     ecClick : function(e){
35769         if(!this.animating && this.node.hasChildNodes()){
35770             this.node.toggle();
35771         }
35772     },
35773
35774     startDrop : function(){
35775         this.dropping = true;
35776     },
35777
35778     // delayed drop so the click event doesn't get fired on a drop
35779     endDrop : function(){
35780        setTimeout(function(){
35781            this.dropping = false;
35782        }.createDelegate(this), 50);
35783     },
35784
35785     expand : function(){
35786         this.updateExpandIcon();
35787         this.ctNode.style.display = "";
35788     },
35789
35790     focus : function(){
35791         if(!this.node.preventHScroll){
35792             try{this.anchor.focus();
35793             }catch(e){}
35794         }else if(!Roo.isIE){
35795             try{
35796                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35797                 var l = noscroll.scrollLeft;
35798                 this.anchor.focus();
35799                 noscroll.scrollLeft = l;
35800             }catch(e){}
35801         }
35802     },
35803
35804     toggleCheck : function(value){
35805         var cb = this.checkbox;
35806         if(cb){
35807             cb.checked = (value === undefined ? !cb.checked : value);
35808         }
35809     },
35810
35811     blur : function(){
35812         try{
35813             this.anchor.blur();
35814         }catch(e){}
35815     },
35816
35817     animExpand : function(callback){
35818         var ct = Roo.get(this.ctNode);
35819         ct.stopFx();
35820         if(!this.node.hasChildNodes()){
35821             this.updateExpandIcon();
35822             this.ctNode.style.display = "";
35823             Roo.callback(callback);
35824             return;
35825         }
35826         this.animating = true;
35827         this.updateExpandIcon();
35828
35829         ct.slideIn('t', {
35830            callback : function(){
35831                this.animating = false;
35832                Roo.callback(callback);
35833             },
35834             scope: this,
35835             duration: this.node.ownerTree.duration || .25
35836         });
35837     },
35838
35839     highlight : function(){
35840         var tree = this.node.getOwnerTree();
35841         Roo.fly(this.wrap).highlight(
35842             tree.hlColor || "C3DAF9",
35843             {endColor: tree.hlBaseColor}
35844         );
35845     },
35846
35847     collapse : function(){
35848         this.updateExpandIcon();
35849         this.ctNode.style.display = "none";
35850     },
35851
35852     animCollapse : function(callback){
35853         var ct = Roo.get(this.ctNode);
35854         ct.enableDisplayMode('block');
35855         ct.stopFx();
35856
35857         this.animating = true;
35858         this.updateExpandIcon();
35859
35860         ct.slideOut('t', {
35861             callback : function(){
35862                this.animating = false;
35863                Roo.callback(callback);
35864             },
35865             scope: this,
35866             duration: this.node.ownerTree.duration || .25
35867         });
35868     },
35869
35870     getContainer : function(){
35871         return this.ctNode;
35872     },
35873
35874     getEl : function(){
35875         return this.wrap;
35876     },
35877
35878     appendDDGhost : function(ghostNode){
35879         ghostNode.appendChild(this.elNode.cloneNode(true));
35880     },
35881
35882     getDDRepairXY : function(){
35883         return Roo.lib.Dom.getXY(this.iconNode);
35884     },
35885
35886     onRender : function(){
35887         this.render();
35888     },
35889
35890     render : function(bulkRender){
35891         var n = this.node, a = n.attributes;
35892         var targetNode = n.parentNode ?
35893               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35894
35895         if(!this.rendered){
35896             this.rendered = true;
35897
35898             this.renderElements(n, a, targetNode, bulkRender);
35899
35900             if(a.qtip){
35901                if(this.textNode.setAttributeNS){
35902                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35903                    if(a.qtipTitle){
35904                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35905                    }
35906                }else{
35907                    this.textNode.setAttribute("ext:qtip", a.qtip);
35908                    if(a.qtipTitle){
35909                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35910                    }
35911                }
35912             }else if(a.qtipCfg){
35913                 a.qtipCfg.target = Roo.id(this.textNode);
35914                 Roo.QuickTips.register(a.qtipCfg);
35915             }
35916             this.initEvents();
35917             if(!this.node.expanded){
35918                 this.updateExpandIcon();
35919             }
35920         }else{
35921             if(bulkRender === true) {
35922                 targetNode.appendChild(this.wrap);
35923             }
35924         }
35925     },
35926
35927     renderElements : function(n, a, targetNode, bulkRender)
35928     {
35929         // add some indent caching, this helps performance when rendering a large tree
35930         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35931         var t = n.getOwnerTree();
35932         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35933         if (typeof(n.attributes.html) != 'undefined') {
35934             txt = n.attributes.html;
35935         }
35936         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35937         var cb = typeof a.checked == 'boolean';
35938         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35939         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35940             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35941             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35942             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35943             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35944             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35945              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35946                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35947             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35948             "</li>"];
35949
35950         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35951             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35952                                 n.nextSibling.ui.getEl(), buf.join(""));
35953         }else{
35954             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35955         }
35956
35957         this.elNode = this.wrap.childNodes[0];
35958         this.ctNode = this.wrap.childNodes[1];
35959         var cs = this.elNode.childNodes;
35960         this.indentNode = cs[0];
35961         this.ecNode = cs[1];
35962         this.iconNode = cs[2];
35963         var index = 3;
35964         if(cb){
35965             this.checkbox = cs[3];
35966             index++;
35967         }
35968         this.anchor = cs[index];
35969         this.textNode = cs[index].firstChild;
35970     },
35971
35972     getAnchor : function(){
35973         return this.anchor;
35974     },
35975
35976     getTextEl : function(){
35977         return this.textNode;
35978     },
35979
35980     getIconEl : function(){
35981         return this.iconNode;
35982     },
35983
35984     isChecked : function(){
35985         return this.checkbox ? this.checkbox.checked : false;
35986     },
35987
35988     updateExpandIcon : function(){
35989         if(this.rendered){
35990             var n = this.node, c1, c2;
35991             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35992             var hasChild = n.hasChildNodes();
35993             if(hasChild){
35994                 if(n.expanded){
35995                     cls += "-minus";
35996                     c1 = "x-tree-node-collapsed";
35997                     c2 = "x-tree-node-expanded";
35998                 }else{
35999                     cls += "-plus";
36000                     c1 = "x-tree-node-expanded";
36001                     c2 = "x-tree-node-collapsed";
36002                 }
36003                 if(this.wasLeaf){
36004                     this.removeClass("x-tree-node-leaf");
36005                     this.wasLeaf = false;
36006                 }
36007                 if(this.c1 != c1 || this.c2 != c2){
36008                     Roo.fly(this.elNode).replaceClass(c1, c2);
36009                     this.c1 = c1; this.c2 = c2;
36010                 }
36011             }else{
36012                 // this changes non-leafs into leafs if they have no children.
36013                 // it's not very rational behaviour..
36014                 
36015                 if(!this.wasLeaf && this.node.leaf){
36016                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36017                     delete this.c1;
36018                     delete this.c2;
36019                     this.wasLeaf = true;
36020                 }
36021             }
36022             var ecc = "x-tree-ec-icon "+cls;
36023             if(this.ecc != ecc){
36024                 this.ecNode.className = ecc;
36025                 this.ecc = ecc;
36026             }
36027         }
36028     },
36029
36030     getChildIndent : function(){
36031         if(!this.childIndent){
36032             var buf = [];
36033             var p = this.node;
36034             while(p){
36035                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36036                     if(!p.isLast()) {
36037                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36038                     } else {
36039                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36040                     }
36041                 }
36042                 p = p.parentNode;
36043             }
36044             this.childIndent = buf.join("");
36045         }
36046         return this.childIndent;
36047     },
36048
36049     renderIndent : function(){
36050         if(this.rendered){
36051             var indent = "";
36052             var p = this.node.parentNode;
36053             if(p){
36054                 indent = p.ui.getChildIndent();
36055             }
36056             if(this.indentMarkup != indent){ // don't rerender if not required
36057                 this.indentNode.innerHTML = indent;
36058                 this.indentMarkup = indent;
36059             }
36060             this.updateExpandIcon();
36061         }
36062     }
36063 };
36064
36065 Roo.tree.RootTreeNodeUI = function(){
36066     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36067 };
36068 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36069     render : function(){
36070         if(!this.rendered){
36071             var targetNode = this.node.ownerTree.innerCt.dom;
36072             this.node.expanded = true;
36073             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36074             this.wrap = this.ctNode = targetNode.firstChild;
36075         }
36076     },
36077     collapse : function(){
36078     },
36079     expand : function(){
36080     }
36081 });/*
36082  * Based on:
36083  * Ext JS Library 1.1.1
36084  * Copyright(c) 2006-2007, Ext JS, LLC.
36085  *
36086  * Originally Released Under LGPL - original licence link has changed is not relivant.
36087  *
36088  * Fork - LGPL
36089  * <script type="text/javascript">
36090  */
36091 /**
36092  * @class Roo.tree.TreeLoader
36093  * @extends Roo.util.Observable
36094  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36095  * nodes from a specified URL. The response must be a javascript Array definition
36096  * who's elements are node definition objects. eg:
36097  * <pre><code>
36098 {  success : true,
36099    data :      [
36100    
36101     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36102     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36103     ]
36104 }
36105
36106
36107 </code></pre>
36108  * <br><br>
36109  * The old style respose with just an array is still supported, but not recommended.
36110  * <br><br>
36111  *
36112  * A server request is sent, and child nodes are loaded only when a node is expanded.
36113  * The loading node's id is passed to the server under the parameter name "node" to
36114  * enable the server to produce the correct child nodes.
36115  * <br><br>
36116  * To pass extra parameters, an event handler may be attached to the "beforeload"
36117  * event, and the parameters specified in the TreeLoader's baseParams property:
36118  * <pre><code>
36119     myTreeLoader.on("beforeload", function(treeLoader, node) {
36120         this.baseParams.category = node.attributes.category;
36121     }, this);
36122     
36123 </code></pre>
36124  *
36125  * This would pass an HTTP parameter called "category" to the server containing
36126  * the value of the Node's "category" attribute.
36127  * @constructor
36128  * Creates a new Treeloader.
36129  * @param {Object} config A config object containing config properties.
36130  */
36131 Roo.tree.TreeLoader = function(config){
36132     this.baseParams = {};
36133     this.requestMethod = "POST";
36134     Roo.apply(this, config);
36135
36136     this.addEvents({
36137     
36138         /**
36139          * @event beforeload
36140          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36141          * @param {Object} This TreeLoader object.
36142          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36143          * @param {Object} callback The callback function specified in the {@link #load} call.
36144          */
36145         beforeload : true,
36146         /**
36147          * @event load
36148          * Fires when the node has been successfuly loaded.
36149          * @param {Object} This TreeLoader object.
36150          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36151          * @param {Object} response The response object containing the data from the server.
36152          */
36153         load : true,
36154         /**
36155          * @event loadexception
36156          * Fires if the network request failed.
36157          * @param {Object} This TreeLoader object.
36158          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36159          * @param {Object} response The response object containing the data from the server.
36160          */
36161         loadexception : true,
36162         /**
36163          * @event create
36164          * Fires before a node is created, enabling you to return custom Node types 
36165          * @param {Object} This TreeLoader object.
36166          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36167          */
36168         create : true
36169     });
36170
36171     Roo.tree.TreeLoader.superclass.constructor.call(this);
36172 };
36173
36174 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36175     /**
36176     * @cfg {String} dataUrl The URL from which to request a Json string which
36177     * specifies an array of node definition object representing the child nodes
36178     * to be loaded.
36179     */
36180     /**
36181     * @cfg {String} requestMethod either GET or POST
36182     * defaults to POST (due to BC)
36183     * to be loaded.
36184     */
36185     /**
36186     * @cfg {Object} baseParams (optional) An object containing properties which
36187     * specify HTTP parameters to be passed to each request for child nodes.
36188     */
36189     /**
36190     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36191     * created by this loader. If the attributes sent by the server have an attribute in this object,
36192     * they take priority.
36193     */
36194     /**
36195     * @cfg {Object} uiProviders (optional) An object containing properties which
36196     * 
36197     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36198     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36199     * <i>uiProvider</i> attribute of a returned child node is a string rather
36200     * than a reference to a TreeNodeUI implementation, this that string value
36201     * is used as a property name in the uiProviders object. You can define the provider named
36202     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36203     */
36204     uiProviders : {},
36205
36206     /**
36207     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36208     * child nodes before loading.
36209     */
36210     clearOnLoad : true,
36211
36212     /**
36213     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36214     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36215     * Grid query { data : [ .....] }
36216     */
36217     
36218     root : false,
36219      /**
36220     * @cfg {String} queryParam (optional) 
36221     * Name of the query as it will be passed on the querystring (defaults to 'node')
36222     * eg. the request will be ?node=[id]
36223     */
36224     
36225     
36226     queryParam: false,
36227     
36228     /**
36229      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36230      * This is called automatically when a node is expanded, but may be used to reload
36231      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36232      * @param {Roo.tree.TreeNode} node
36233      * @param {Function} callback
36234      */
36235     load : function(node, callback){
36236         if(this.clearOnLoad){
36237             while(node.firstChild){
36238                 node.removeChild(node.firstChild);
36239             }
36240         }
36241         if(node.attributes.children){ // preloaded json children
36242             var cs = node.attributes.children;
36243             for(var i = 0, len = cs.length; i < len; i++){
36244                 node.appendChild(this.createNode(cs[i]));
36245             }
36246             if(typeof callback == "function"){
36247                 callback();
36248             }
36249         }else if(this.dataUrl){
36250             this.requestData(node, callback);
36251         }
36252     },
36253
36254     getParams: function(node){
36255         var buf = [], bp = this.baseParams;
36256         for(var key in bp){
36257             if(typeof bp[key] != "function"){
36258                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36259             }
36260         }
36261         var n = this.queryParam === false ? 'node' : this.queryParam;
36262         buf.push(n + "=", encodeURIComponent(node.id));
36263         return buf.join("");
36264     },
36265
36266     requestData : function(node, callback){
36267         if(this.fireEvent("beforeload", this, node, callback) !== false){
36268             this.transId = Roo.Ajax.request({
36269                 method:this.requestMethod,
36270                 url: this.dataUrl||this.url,
36271                 success: this.handleResponse,
36272                 failure: this.handleFailure,
36273                 scope: this,
36274                 argument: {callback: callback, node: node},
36275                 params: this.getParams(node)
36276             });
36277         }else{
36278             // if the load is cancelled, make sure we notify
36279             // the node that we are done
36280             if(typeof callback == "function"){
36281                 callback();
36282             }
36283         }
36284     },
36285
36286     isLoading : function(){
36287         return this.transId ? true : false;
36288     },
36289
36290     abort : function(){
36291         if(this.isLoading()){
36292             Roo.Ajax.abort(this.transId);
36293         }
36294     },
36295
36296     // private
36297     createNode : function(attr)
36298     {
36299         // apply baseAttrs, nice idea Corey!
36300         if(this.baseAttrs){
36301             Roo.applyIf(attr, this.baseAttrs);
36302         }
36303         if(this.applyLoader !== false){
36304             attr.loader = this;
36305         }
36306         // uiProvider = depreciated..
36307         
36308         if(typeof(attr.uiProvider) == 'string'){
36309            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36310                 /**  eval:var:attr */ eval(attr.uiProvider);
36311         }
36312         if(typeof(this.uiProviders['default']) != 'undefined') {
36313             attr.uiProvider = this.uiProviders['default'];
36314         }
36315         
36316         this.fireEvent('create', this, attr);
36317         
36318         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36319         return(attr.leaf ?
36320                         new Roo.tree.TreeNode(attr) :
36321                         new Roo.tree.AsyncTreeNode(attr));
36322     },
36323
36324     processResponse : function(response, node, callback)
36325     {
36326         var json = response.responseText;
36327         try {
36328             
36329             var o = Roo.decode(json);
36330             
36331             if (this.root === false && typeof(o.success) != undefined) {
36332                 this.root = 'data'; // the default behaviour for list like data..
36333                 }
36334                 
36335             if (this.root !== false &&  !o.success) {
36336                 // it's a failure condition.
36337                 var a = response.argument;
36338                 this.fireEvent("loadexception", this, a.node, response);
36339                 Roo.log("Load failed - should have a handler really");
36340                 return;
36341             }
36342             
36343             
36344             
36345             if (this.root !== false) {
36346                  o = o[this.root];
36347             }
36348             
36349             for(var i = 0, len = o.length; i < len; i++){
36350                 var n = this.createNode(o[i]);
36351                 if(n){
36352                     node.appendChild(n);
36353                 }
36354             }
36355             if(typeof callback == "function"){
36356                 callback(this, node);
36357             }
36358         }catch(e){
36359             this.handleFailure(response);
36360         }
36361     },
36362
36363     handleResponse : function(response){
36364         this.transId = false;
36365         var a = response.argument;
36366         this.processResponse(response, a.node, a.callback);
36367         this.fireEvent("load", this, a.node, response);
36368     },
36369
36370     handleFailure : function(response)
36371     {
36372         // should handle failure better..
36373         this.transId = false;
36374         var a = response.argument;
36375         this.fireEvent("loadexception", this, a.node, response);
36376         if(typeof a.callback == "function"){
36377             a.callback(this, a.node);
36378         }
36379     }
36380 });/*
36381  * Based on:
36382  * Ext JS Library 1.1.1
36383  * Copyright(c) 2006-2007, Ext JS, LLC.
36384  *
36385  * Originally Released Under LGPL - original licence link has changed is not relivant.
36386  *
36387  * Fork - LGPL
36388  * <script type="text/javascript">
36389  */
36390
36391 /**
36392 * @class Roo.tree.TreeFilter
36393 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36394 * @param {TreePanel} tree
36395 * @param {Object} config (optional)
36396  */
36397 Roo.tree.TreeFilter = function(tree, config){
36398     this.tree = tree;
36399     this.filtered = {};
36400     Roo.apply(this, config);
36401 };
36402
36403 Roo.tree.TreeFilter.prototype = {
36404     clearBlank:false,
36405     reverse:false,
36406     autoClear:false,
36407     remove:false,
36408
36409      /**
36410      * Filter the data by a specific attribute.
36411      * @param {String/RegExp} value Either string that the attribute value
36412      * should start with or a RegExp to test against the attribute
36413      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36414      * @param {TreeNode} startNode (optional) The node to start the filter at.
36415      */
36416     filter : function(value, attr, startNode){
36417         attr = attr || "text";
36418         var f;
36419         if(typeof value == "string"){
36420             var vlen = value.length;
36421             // auto clear empty filter
36422             if(vlen == 0 && this.clearBlank){
36423                 this.clear();
36424                 return;
36425             }
36426             value = value.toLowerCase();
36427             f = function(n){
36428                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36429             };
36430         }else if(value.exec){ // regex?
36431             f = function(n){
36432                 return value.test(n.attributes[attr]);
36433             };
36434         }else{
36435             throw 'Illegal filter type, must be string or regex';
36436         }
36437         this.filterBy(f, null, startNode);
36438         },
36439
36440     /**
36441      * Filter by a function. The passed function will be called with each
36442      * node in the tree (or from the startNode). If the function returns true, the node is kept
36443      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36444      * @param {Function} fn The filter function
36445      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36446      */
36447     filterBy : function(fn, scope, startNode){
36448         startNode = startNode || this.tree.root;
36449         if(this.autoClear){
36450             this.clear();
36451         }
36452         var af = this.filtered, rv = this.reverse;
36453         var f = function(n){
36454             if(n == startNode){
36455                 return true;
36456             }
36457             if(af[n.id]){
36458                 return false;
36459             }
36460             var m = fn.call(scope || n, n);
36461             if(!m || rv){
36462                 af[n.id] = n;
36463                 n.ui.hide();
36464                 return false;
36465             }
36466             return true;
36467         };
36468         startNode.cascade(f);
36469         if(this.remove){
36470            for(var id in af){
36471                if(typeof id != "function"){
36472                    var n = af[id];
36473                    if(n && n.parentNode){
36474                        n.parentNode.removeChild(n);
36475                    }
36476                }
36477            }
36478         }
36479     },
36480
36481     /**
36482      * Clears the current filter. Note: with the "remove" option
36483      * set a filter cannot be cleared.
36484      */
36485     clear : function(){
36486         var t = this.tree;
36487         var af = this.filtered;
36488         for(var id in af){
36489             if(typeof id != "function"){
36490                 var n = af[id];
36491                 if(n){
36492                     n.ui.show();
36493                 }
36494             }
36495         }
36496         this.filtered = {};
36497     }
36498 };
36499 /*
36500  * Based on:
36501  * Ext JS Library 1.1.1
36502  * Copyright(c) 2006-2007, Ext JS, LLC.
36503  *
36504  * Originally Released Under LGPL - original licence link has changed is not relivant.
36505  *
36506  * Fork - LGPL
36507  * <script type="text/javascript">
36508  */
36509  
36510
36511 /**
36512  * @class Roo.tree.TreeSorter
36513  * Provides sorting of nodes in a TreePanel
36514  * 
36515  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36516  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36517  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36518  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36519  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36520  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36521  * @constructor
36522  * @param {TreePanel} tree
36523  * @param {Object} config
36524  */
36525 Roo.tree.TreeSorter = function(tree, config){
36526     Roo.apply(this, config);
36527     tree.on("beforechildrenrendered", this.doSort, this);
36528     tree.on("append", this.updateSort, this);
36529     tree.on("insert", this.updateSort, this);
36530     
36531     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36532     var p = this.property || "text";
36533     var sortType = this.sortType;
36534     var fs = this.folderSort;
36535     var cs = this.caseSensitive === true;
36536     var leafAttr = this.leafAttr || 'leaf';
36537
36538     this.sortFn = function(n1, n2){
36539         if(fs){
36540             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36541                 return 1;
36542             }
36543             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36544                 return -1;
36545             }
36546         }
36547         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36548         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36549         if(v1 < v2){
36550                         return dsc ? +1 : -1;
36551                 }else if(v1 > v2){
36552                         return dsc ? -1 : +1;
36553         }else{
36554                 return 0;
36555         }
36556     };
36557 };
36558
36559 Roo.tree.TreeSorter.prototype = {
36560     doSort : function(node){
36561         node.sort(this.sortFn);
36562     },
36563     
36564     compareNodes : function(n1, n2){
36565         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36566     },
36567     
36568     updateSort : function(tree, node){
36569         if(node.childrenRendered){
36570             this.doSort.defer(1, this, [node]);
36571         }
36572     }
36573 };/*
36574  * Based on:
36575  * Ext JS Library 1.1.1
36576  * Copyright(c) 2006-2007, Ext JS, LLC.
36577  *
36578  * Originally Released Under LGPL - original licence link has changed is not relivant.
36579  *
36580  * Fork - LGPL
36581  * <script type="text/javascript">
36582  */
36583
36584 if(Roo.dd.DropZone){
36585     
36586 Roo.tree.TreeDropZone = function(tree, config){
36587     this.allowParentInsert = false;
36588     this.allowContainerDrop = false;
36589     this.appendOnly = false;
36590     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36591     this.tree = tree;
36592     this.lastInsertClass = "x-tree-no-status";
36593     this.dragOverData = {};
36594 };
36595
36596 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36597     ddGroup : "TreeDD",
36598     scroll:  true,
36599     
36600     expandDelay : 1000,
36601     
36602     expandNode : function(node){
36603         if(node.hasChildNodes() && !node.isExpanded()){
36604             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36605         }
36606     },
36607     
36608     queueExpand : function(node){
36609         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36610     },
36611     
36612     cancelExpand : function(){
36613         if(this.expandProcId){
36614             clearTimeout(this.expandProcId);
36615             this.expandProcId = false;
36616         }
36617     },
36618     
36619     isValidDropPoint : function(n, pt, dd, e, data){
36620         if(!n || !data){ return false; }
36621         var targetNode = n.node;
36622         var dropNode = data.node;
36623         // default drop rules
36624         if(!(targetNode && targetNode.isTarget && pt)){
36625             return false;
36626         }
36627         if(pt == "append" && targetNode.allowChildren === false){
36628             return false;
36629         }
36630         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36631             return false;
36632         }
36633         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36634             return false;
36635         }
36636         // reuse the object
36637         var overEvent = this.dragOverData;
36638         overEvent.tree = this.tree;
36639         overEvent.target = targetNode;
36640         overEvent.data = data;
36641         overEvent.point = pt;
36642         overEvent.source = dd;
36643         overEvent.rawEvent = e;
36644         overEvent.dropNode = dropNode;
36645         overEvent.cancel = false;  
36646         var result = this.tree.fireEvent("nodedragover", overEvent);
36647         return overEvent.cancel === false && result !== false;
36648     },
36649     
36650     getDropPoint : function(e, n, dd)
36651     {
36652         var tn = n.node;
36653         if(tn.isRoot){
36654             return tn.allowChildren !== false ? "append" : false; // always append for root
36655         }
36656         var dragEl = n.ddel;
36657         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36658         var y = Roo.lib.Event.getPageY(e);
36659         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36660         
36661         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36662         var noAppend = tn.allowChildren === false;
36663         if(this.appendOnly || tn.parentNode.allowChildren === false){
36664             return noAppend ? false : "append";
36665         }
36666         var noBelow = false;
36667         if(!this.allowParentInsert){
36668             noBelow = tn.hasChildNodes() && tn.isExpanded();
36669         }
36670         var q = (b - t) / (noAppend ? 2 : 3);
36671         if(y >= t && y < (t + q)){
36672             return "above";
36673         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36674             return "below";
36675         }else{
36676             return "append";
36677         }
36678     },
36679     
36680     onNodeEnter : function(n, dd, e, data)
36681     {
36682         this.cancelExpand();
36683     },
36684     
36685     onNodeOver : function(n, dd, e, data)
36686     {
36687        
36688         var pt = this.getDropPoint(e, n, dd);
36689         var node = n.node;
36690         
36691         // auto node expand check
36692         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36693             this.queueExpand(node);
36694         }else if(pt != "append"){
36695             this.cancelExpand();
36696         }
36697         
36698         // set the insert point style on the target node
36699         var returnCls = this.dropNotAllowed;
36700         if(this.isValidDropPoint(n, pt, dd, e, data)){
36701            if(pt){
36702                var el = n.ddel;
36703                var cls;
36704                if(pt == "above"){
36705                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36706                    cls = "x-tree-drag-insert-above";
36707                }else if(pt == "below"){
36708                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36709                    cls = "x-tree-drag-insert-below";
36710                }else{
36711                    returnCls = "x-tree-drop-ok-append";
36712                    cls = "x-tree-drag-append";
36713                }
36714                if(this.lastInsertClass != cls){
36715                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36716                    this.lastInsertClass = cls;
36717                }
36718            }
36719        }
36720        return returnCls;
36721     },
36722     
36723     onNodeOut : function(n, dd, e, data){
36724         
36725         this.cancelExpand();
36726         this.removeDropIndicators(n);
36727     },
36728     
36729     onNodeDrop : function(n, dd, e, data){
36730         var point = this.getDropPoint(e, n, dd);
36731         var targetNode = n.node;
36732         targetNode.ui.startDrop();
36733         if(!this.isValidDropPoint(n, point, dd, e, data)){
36734             targetNode.ui.endDrop();
36735             return false;
36736         }
36737         // first try to find the drop node
36738         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36739         var dropEvent = {
36740             tree : this.tree,
36741             target: targetNode,
36742             data: data,
36743             point: point,
36744             source: dd,
36745             rawEvent: e,
36746             dropNode: dropNode,
36747             cancel: !dropNode   
36748         };
36749         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36750         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36751             targetNode.ui.endDrop();
36752             return false;
36753         }
36754         // allow target changing
36755         targetNode = dropEvent.target;
36756         if(point == "append" && !targetNode.isExpanded()){
36757             targetNode.expand(false, null, function(){
36758                 this.completeDrop(dropEvent);
36759             }.createDelegate(this));
36760         }else{
36761             this.completeDrop(dropEvent);
36762         }
36763         return true;
36764     },
36765     
36766     completeDrop : function(de){
36767         var ns = de.dropNode, p = de.point, t = de.target;
36768         if(!(ns instanceof Array)){
36769             ns = [ns];
36770         }
36771         var n;
36772         for(var i = 0, len = ns.length; i < len; i++){
36773             n = ns[i];
36774             if(p == "above"){
36775                 t.parentNode.insertBefore(n, t);
36776             }else if(p == "below"){
36777                 t.parentNode.insertBefore(n, t.nextSibling);
36778             }else{
36779                 t.appendChild(n);
36780             }
36781         }
36782         n.ui.focus();
36783         if(this.tree.hlDrop){
36784             n.ui.highlight();
36785         }
36786         t.ui.endDrop();
36787         this.tree.fireEvent("nodedrop", de);
36788     },
36789     
36790     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36791         if(this.tree.hlDrop){
36792             dropNode.ui.focus();
36793             dropNode.ui.highlight();
36794         }
36795         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36796     },
36797     
36798     getTree : function(){
36799         return this.tree;
36800     },
36801     
36802     removeDropIndicators : function(n){
36803         if(n && n.ddel){
36804             var el = n.ddel;
36805             Roo.fly(el).removeClass([
36806                     "x-tree-drag-insert-above",
36807                     "x-tree-drag-insert-below",
36808                     "x-tree-drag-append"]);
36809             this.lastInsertClass = "_noclass";
36810         }
36811     },
36812     
36813     beforeDragDrop : function(target, e, id){
36814         this.cancelExpand();
36815         return true;
36816     },
36817     
36818     afterRepair : function(data){
36819         if(data && Roo.enableFx){
36820             data.node.ui.highlight();
36821         }
36822         this.hideProxy();
36823     } 
36824     
36825 });
36826
36827 }
36828 /*
36829  * Based on:
36830  * Ext JS Library 1.1.1
36831  * Copyright(c) 2006-2007, Ext JS, LLC.
36832  *
36833  * Originally Released Under LGPL - original licence link has changed is not relivant.
36834  *
36835  * Fork - LGPL
36836  * <script type="text/javascript">
36837  */
36838  
36839
36840 if(Roo.dd.DragZone){
36841 Roo.tree.TreeDragZone = function(tree, config){
36842     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36843     this.tree = tree;
36844 };
36845
36846 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36847     ddGroup : "TreeDD",
36848    
36849     onBeforeDrag : function(data, e){
36850         var n = data.node;
36851         return n && n.draggable && !n.disabled;
36852     },
36853      
36854     
36855     onInitDrag : function(e){
36856         var data = this.dragData;
36857         this.tree.getSelectionModel().select(data.node);
36858         this.proxy.update("");
36859         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36860         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36861     },
36862     
36863     getRepairXY : function(e, data){
36864         return data.node.ui.getDDRepairXY();
36865     },
36866     
36867     onEndDrag : function(data, e){
36868         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36869         
36870         
36871     },
36872     
36873     onValidDrop : function(dd, e, id){
36874         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36875         this.hideProxy();
36876     },
36877     
36878     beforeInvalidDrop : function(e, id){
36879         // this scrolls the original position back into view
36880         var sm = this.tree.getSelectionModel();
36881         sm.clearSelections();
36882         sm.select(this.dragData.node);
36883     }
36884 });
36885 }/*
36886  * Based on:
36887  * Ext JS Library 1.1.1
36888  * Copyright(c) 2006-2007, Ext JS, LLC.
36889  *
36890  * Originally Released Under LGPL - original licence link has changed is not relivant.
36891  *
36892  * Fork - LGPL
36893  * <script type="text/javascript">
36894  */
36895 /**
36896  * @class Roo.tree.TreeEditor
36897  * @extends Roo.Editor
36898  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36899  * as the editor field.
36900  * @constructor
36901  * @param {Object} config (used to be the tree panel.)
36902  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36903  * 
36904  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36905  * @cfg {Roo.form.TextField|Object} field The field configuration
36906  *
36907  * 
36908  */
36909 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36910     var tree = config;
36911     var field;
36912     if (oldconfig) { // old style..
36913         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36914     } else {
36915         // new style..
36916         tree = config.tree;
36917         config.field = config.field  || {};
36918         config.field.xtype = 'TextField';
36919         field = Roo.factory(config.field, Roo.form);
36920     }
36921     config = config || {};
36922     
36923     
36924     this.addEvents({
36925         /**
36926          * @event beforenodeedit
36927          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36928          * false from the handler of this event.
36929          * @param {Editor} this
36930          * @param {Roo.tree.Node} node 
36931          */
36932         "beforenodeedit" : true
36933     });
36934     
36935     //Roo.log(config);
36936     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36937
36938     this.tree = tree;
36939
36940     tree.on('beforeclick', this.beforeNodeClick, this);
36941     tree.getTreeEl().on('mousedown', this.hide, this);
36942     this.on('complete', this.updateNode, this);
36943     this.on('beforestartedit', this.fitToTree, this);
36944     this.on('startedit', this.bindScroll, this, {delay:10});
36945     this.on('specialkey', this.onSpecialKey, this);
36946 };
36947
36948 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36949     /**
36950      * @cfg {String} alignment
36951      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36952      */
36953     alignment: "l-l",
36954     // inherit
36955     autoSize: false,
36956     /**
36957      * @cfg {Boolean} hideEl
36958      * True to hide the bound element while the editor is displayed (defaults to false)
36959      */
36960     hideEl : false,
36961     /**
36962      * @cfg {String} cls
36963      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36964      */
36965     cls: "x-small-editor x-tree-editor",
36966     /**
36967      * @cfg {Boolean} shim
36968      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36969      */
36970     shim:false,
36971     // inherit
36972     shadow:"frame",
36973     /**
36974      * @cfg {Number} maxWidth
36975      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36976      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36977      * scroll and client offsets into account prior to each edit.
36978      */
36979     maxWidth: 250,
36980
36981     editDelay : 350,
36982
36983     // private
36984     fitToTree : function(ed, el){
36985         var td = this.tree.getTreeEl().dom, nd = el.dom;
36986         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36987             td.scrollLeft = nd.offsetLeft;
36988         }
36989         var w = Math.min(
36990                 this.maxWidth,
36991                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36992         this.setSize(w, '');
36993         
36994         return this.fireEvent('beforenodeedit', this, this.editNode);
36995         
36996     },
36997
36998     // private
36999     triggerEdit : function(node){
37000         this.completeEdit();
37001         this.editNode = node;
37002         this.startEdit(node.ui.textNode, node.text);
37003     },
37004
37005     // private
37006     bindScroll : function(){
37007         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37008     },
37009
37010     // private
37011     beforeNodeClick : function(node, e){
37012         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37013         this.lastClick = new Date();
37014         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37015             e.stopEvent();
37016             this.triggerEdit(node);
37017             return false;
37018         }
37019         return true;
37020     },
37021
37022     // private
37023     updateNode : function(ed, value){
37024         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37025         this.editNode.setText(value);
37026     },
37027
37028     // private
37029     onHide : function(){
37030         Roo.tree.TreeEditor.superclass.onHide.call(this);
37031         if(this.editNode){
37032             this.editNode.ui.focus();
37033         }
37034     },
37035
37036     // private
37037     onSpecialKey : function(field, e){
37038         var k = e.getKey();
37039         if(k == e.ESC){
37040             e.stopEvent();
37041             this.cancelEdit();
37042         }else if(k == e.ENTER && !e.hasModifier()){
37043             e.stopEvent();
37044             this.completeEdit();
37045         }
37046     }
37047 });//<Script type="text/javascript">
37048 /*
37049  * Based on:
37050  * Ext JS Library 1.1.1
37051  * Copyright(c) 2006-2007, Ext JS, LLC.
37052  *
37053  * Originally Released Under LGPL - original licence link has changed is not relivant.
37054  *
37055  * Fork - LGPL
37056  * <script type="text/javascript">
37057  */
37058  
37059 /**
37060  * Not documented??? - probably should be...
37061  */
37062
37063 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37064     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37065     
37066     renderElements : function(n, a, targetNode, bulkRender){
37067         //consel.log("renderElements?");
37068         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37069
37070         var t = n.getOwnerTree();
37071         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37072         
37073         var cols = t.columns;
37074         var bw = t.borderWidth;
37075         var c = cols[0];
37076         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37077          var cb = typeof a.checked == "boolean";
37078         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37079         var colcls = 'x-t-' + tid + '-c0';
37080         var buf = [
37081             '<li class="x-tree-node">',
37082             
37083                 
37084                 '<div class="x-tree-node-el ', a.cls,'">',
37085                     // extran...
37086                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37087                 
37088                 
37089                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37090                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37091                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37092                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37093                            (a.iconCls ? ' '+a.iconCls : ''),
37094                            '" unselectable="on" />',
37095                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37096                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37097                              
37098                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37099                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37100                             '<span unselectable="on" qtip="' + tx + '">',
37101                              tx,
37102                              '</span></a>' ,
37103                     '</div>',
37104                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37105                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37106                  ];
37107         for(var i = 1, len = cols.length; i < len; i++){
37108             c = cols[i];
37109             colcls = 'x-t-' + tid + '-c' +i;
37110             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37111             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37112                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37113                       "</div>");
37114          }
37115          
37116          buf.push(
37117             '</a>',
37118             '<div class="x-clear"></div></div>',
37119             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37120             "</li>");
37121         
37122         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37123             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37124                                 n.nextSibling.ui.getEl(), buf.join(""));
37125         }else{
37126             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37127         }
37128         var el = this.wrap.firstChild;
37129         this.elRow = el;
37130         this.elNode = el.firstChild;
37131         this.ranchor = el.childNodes[1];
37132         this.ctNode = this.wrap.childNodes[1];
37133         var cs = el.firstChild.childNodes;
37134         this.indentNode = cs[0];
37135         this.ecNode = cs[1];
37136         this.iconNode = cs[2];
37137         var index = 3;
37138         if(cb){
37139             this.checkbox = cs[3];
37140             index++;
37141         }
37142         this.anchor = cs[index];
37143         
37144         this.textNode = cs[index].firstChild;
37145         
37146         //el.on("click", this.onClick, this);
37147         //el.on("dblclick", this.onDblClick, this);
37148         
37149         
37150        // console.log(this);
37151     },
37152     initEvents : function(){
37153         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37154         
37155             
37156         var a = this.ranchor;
37157
37158         var el = Roo.get(a);
37159
37160         if(Roo.isOpera){ // opera render bug ignores the CSS
37161             el.setStyle("text-decoration", "none");
37162         }
37163
37164         el.on("click", this.onClick, this);
37165         el.on("dblclick", this.onDblClick, this);
37166         el.on("contextmenu", this.onContextMenu, this);
37167         
37168     },
37169     
37170     /*onSelectedChange : function(state){
37171         if(state){
37172             this.focus();
37173             this.addClass("x-tree-selected");
37174         }else{
37175             //this.blur();
37176             this.removeClass("x-tree-selected");
37177         }
37178     },*/
37179     addClass : function(cls){
37180         if(this.elRow){
37181             Roo.fly(this.elRow).addClass(cls);
37182         }
37183         
37184     },
37185     
37186     
37187     removeClass : function(cls){
37188         if(this.elRow){
37189             Roo.fly(this.elRow).removeClass(cls);
37190         }
37191     }
37192
37193     
37194     
37195 });//<Script type="text/javascript">
37196
37197 /*
37198  * Based on:
37199  * Ext JS Library 1.1.1
37200  * Copyright(c) 2006-2007, Ext JS, LLC.
37201  *
37202  * Originally Released Under LGPL - original licence link has changed is not relivant.
37203  *
37204  * Fork - LGPL
37205  * <script type="text/javascript">
37206  */
37207  
37208
37209 /**
37210  * @class Roo.tree.ColumnTree
37211  * @extends Roo.data.TreePanel
37212  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37213  * @cfg {int} borderWidth  compined right/left border allowance
37214  * @constructor
37215  * @param {String/HTMLElement/Element} el The container element
37216  * @param {Object} config
37217  */
37218 Roo.tree.ColumnTree =  function(el, config)
37219 {
37220    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37221    this.addEvents({
37222         /**
37223         * @event resize
37224         * Fire this event on a container when it resizes
37225         * @param {int} w Width
37226         * @param {int} h Height
37227         */
37228        "resize" : true
37229     });
37230     this.on('resize', this.onResize, this);
37231 };
37232
37233 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37234     //lines:false,
37235     
37236     
37237     borderWidth: Roo.isBorderBox ? 0 : 2, 
37238     headEls : false,
37239     
37240     render : function(){
37241         // add the header.....
37242        
37243         Roo.tree.ColumnTree.superclass.render.apply(this);
37244         
37245         this.el.addClass('x-column-tree');
37246         
37247         this.headers = this.el.createChild(
37248             {cls:'x-tree-headers'},this.innerCt.dom);
37249    
37250         var cols = this.columns, c;
37251         var totalWidth = 0;
37252         this.headEls = [];
37253         var  len = cols.length;
37254         for(var i = 0; i < len; i++){
37255              c = cols[i];
37256              totalWidth += c.width;
37257             this.headEls.push(this.headers.createChild({
37258                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37259                  cn: {
37260                      cls:'x-tree-hd-text',
37261                      html: c.header
37262                  },
37263                  style:'width:'+(c.width-this.borderWidth)+'px;'
37264              }));
37265         }
37266         this.headers.createChild({cls:'x-clear'});
37267         // prevent floats from wrapping when clipped
37268         this.headers.setWidth(totalWidth);
37269         //this.innerCt.setWidth(totalWidth);
37270         this.innerCt.setStyle({ overflow: 'auto' });
37271         this.onResize(this.width, this.height);
37272              
37273         
37274     },
37275     onResize : function(w,h)
37276     {
37277         this.height = h;
37278         this.width = w;
37279         // resize cols..
37280         this.innerCt.setWidth(this.width);
37281         this.innerCt.setHeight(this.height-20);
37282         
37283         // headers...
37284         var cols = this.columns, c;
37285         var totalWidth = 0;
37286         var expEl = false;
37287         var len = cols.length;
37288         for(var i = 0; i < len; i++){
37289             c = cols[i];
37290             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37291                 // it's the expander..
37292                 expEl  = this.headEls[i];
37293                 continue;
37294             }
37295             totalWidth += c.width;
37296             
37297         }
37298         if (expEl) {
37299             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37300         }
37301         this.headers.setWidth(w-20);
37302
37303         
37304         
37305         
37306     }
37307 });
37308 /*
37309  * Based on:
37310  * Ext JS Library 1.1.1
37311  * Copyright(c) 2006-2007, Ext JS, LLC.
37312  *
37313  * Originally Released Under LGPL - original licence link has changed is not relivant.
37314  *
37315  * Fork - LGPL
37316  * <script type="text/javascript">
37317  */
37318  
37319 /**
37320  * @class Roo.menu.Menu
37321  * @extends Roo.util.Observable
37322  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37323  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37324  * @constructor
37325  * Creates a new Menu
37326  * @param {Object} config Configuration options
37327  */
37328 Roo.menu.Menu = function(config){
37329     
37330     Roo.menu.Menu.superclass.constructor.call(this, config);
37331     
37332     this.id = this.id || Roo.id();
37333     this.addEvents({
37334         /**
37335          * @event beforeshow
37336          * Fires before this menu is displayed
37337          * @param {Roo.menu.Menu} this
37338          */
37339         beforeshow : true,
37340         /**
37341          * @event beforehide
37342          * Fires before this menu is hidden
37343          * @param {Roo.menu.Menu} this
37344          */
37345         beforehide : true,
37346         /**
37347          * @event show
37348          * Fires after this menu is displayed
37349          * @param {Roo.menu.Menu} this
37350          */
37351         show : true,
37352         /**
37353          * @event hide
37354          * Fires after this menu is hidden
37355          * @param {Roo.menu.Menu} this
37356          */
37357         hide : true,
37358         /**
37359          * @event click
37360          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37361          * @param {Roo.menu.Menu} this
37362          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37363          * @param {Roo.EventObject} e
37364          */
37365         click : true,
37366         /**
37367          * @event mouseover
37368          * Fires when the mouse is hovering over this menu
37369          * @param {Roo.menu.Menu} this
37370          * @param {Roo.EventObject} e
37371          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37372          */
37373         mouseover : true,
37374         /**
37375          * @event mouseout
37376          * Fires when the mouse exits this menu
37377          * @param {Roo.menu.Menu} this
37378          * @param {Roo.EventObject} e
37379          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37380          */
37381         mouseout : true,
37382         /**
37383          * @event itemclick
37384          * Fires when a menu item contained in this menu is clicked
37385          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37386          * @param {Roo.EventObject} e
37387          */
37388         itemclick: true
37389     });
37390     if (this.registerMenu) {
37391         Roo.menu.MenuMgr.register(this);
37392     }
37393     
37394     var mis = this.items;
37395     this.items = new Roo.util.MixedCollection();
37396     if(mis){
37397         this.add.apply(this, mis);
37398     }
37399 };
37400
37401 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37402     /**
37403      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37404      */
37405     minWidth : 120,
37406     /**
37407      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37408      * for bottom-right shadow (defaults to "sides")
37409      */
37410     shadow : "sides",
37411     /**
37412      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37413      * this menu (defaults to "tl-tr?")
37414      */
37415     subMenuAlign : "tl-tr?",
37416     /**
37417      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37418      * relative to its element of origin (defaults to "tl-bl?")
37419      */
37420     defaultAlign : "tl-bl?",
37421     /**
37422      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37423      */
37424     allowOtherMenus : false,
37425     /**
37426      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37427      */
37428     registerMenu : true,
37429
37430     hidden:true,
37431
37432     // private
37433     render : function(){
37434         if(this.el){
37435             return;
37436         }
37437         var el = this.el = new Roo.Layer({
37438             cls: "x-menu",
37439             shadow:this.shadow,
37440             constrain: false,
37441             parentEl: this.parentEl || document.body,
37442             zindex:15000
37443         });
37444
37445         this.keyNav = new Roo.menu.MenuNav(this);
37446
37447         if(this.plain){
37448             el.addClass("x-menu-plain");
37449         }
37450         if(this.cls){
37451             el.addClass(this.cls);
37452         }
37453         // generic focus element
37454         this.focusEl = el.createChild({
37455             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37456         });
37457         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37458         //disabling touch- as it's causing issues ..
37459         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37460         ul.on('click'   , this.onClick, this);
37461         
37462         
37463         ul.on("mouseover", this.onMouseOver, this);
37464         ul.on("mouseout", this.onMouseOut, this);
37465         this.items.each(function(item){
37466             if (item.hidden) {
37467                 return;
37468             }
37469             
37470             var li = document.createElement("li");
37471             li.className = "x-menu-list-item";
37472             ul.dom.appendChild(li);
37473             item.render(li, this);
37474         }, this);
37475         this.ul = ul;
37476         this.autoWidth();
37477     },
37478
37479     // private
37480     autoWidth : function(){
37481         var el = this.el, ul = this.ul;
37482         if(!el){
37483             return;
37484         }
37485         var w = this.width;
37486         if(w){
37487             el.setWidth(w);
37488         }else if(Roo.isIE){
37489             el.setWidth(this.minWidth);
37490             var t = el.dom.offsetWidth; // force recalc
37491             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37492         }
37493     },
37494
37495     // private
37496     delayAutoWidth : function(){
37497         if(this.rendered){
37498             if(!this.awTask){
37499                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37500             }
37501             this.awTask.delay(20);
37502         }
37503     },
37504
37505     // private
37506     findTargetItem : function(e){
37507         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37508         if(t && t.menuItemId){
37509             return this.items.get(t.menuItemId);
37510         }
37511     },
37512
37513     // private
37514     onClick : function(e){
37515         Roo.log("menu.onClick");
37516         var t = this.findTargetItem(e);
37517         if(!t){
37518             return;
37519         }
37520         Roo.log(e);
37521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37522             if(t == this.activeItem && t.shouldDeactivate(e)){
37523                 this.activeItem.deactivate();
37524                 delete this.activeItem;
37525                 return;
37526             }
37527             if(t.canActivate){
37528                 this.setActiveItem(t, true);
37529             }
37530             return;
37531             
37532             
37533         }
37534         
37535         t.onClick(e);
37536         this.fireEvent("click", this, t, e);
37537     },
37538
37539     // private
37540     setActiveItem : function(item, autoExpand){
37541         if(item != this.activeItem){
37542             if(this.activeItem){
37543                 this.activeItem.deactivate();
37544             }
37545             this.activeItem = item;
37546             item.activate(autoExpand);
37547         }else if(autoExpand){
37548             item.expandMenu();
37549         }
37550     },
37551
37552     // private
37553     tryActivate : function(start, step){
37554         var items = this.items;
37555         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37556             var item = items.get(i);
37557             if(!item.disabled && item.canActivate){
37558                 this.setActiveItem(item, false);
37559                 return item;
37560             }
37561         }
37562         return false;
37563     },
37564
37565     // private
37566     onMouseOver : function(e){
37567         var t;
37568         if(t = this.findTargetItem(e)){
37569             if(t.canActivate && !t.disabled){
37570                 this.setActiveItem(t, true);
37571             }
37572         }
37573         this.fireEvent("mouseover", this, e, t);
37574     },
37575
37576     // private
37577     onMouseOut : function(e){
37578         var t;
37579         if(t = this.findTargetItem(e)){
37580             if(t == this.activeItem && t.shouldDeactivate(e)){
37581                 this.activeItem.deactivate();
37582                 delete this.activeItem;
37583             }
37584         }
37585         this.fireEvent("mouseout", this, e, t);
37586     },
37587
37588     /**
37589      * Read-only.  Returns true if the menu is currently displayed, else false.
37590      * @type Boolean
37591      */
37592     isVisible : function(){
37593         return this.el && !this.hidden;
37594     },
37595
37596     /**
37597      * Displays this menu relative to another element
37598      * @param {String/HTMLElement/Roo.Element} element The element to align to
37599      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37600      * the element (defaults to this.defaultAlign)
37601      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37602      */
37603     show : function(el, pos, parentMenu){
37604         this.parentMenu = parentMenu;
37605         if(!this.el){
37606             this.render();
37607         }
37608         this.fireEvent("beforeshow", this);
37609         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37610     },
37611
37612     /**
37613      * Displays this menu at a specific xy position
37614      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37615      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37616      */
37617     showAt : function(xy, parentMenu, /* private: */_e){
37618         this.parentMenu = parentMenu;
37619         if(!this.el){
37620             this.render();
37621         }
37622         if(_e !== false){
37623             this.fireEvent("beforeshow", this);
37624             xy = this.el.adjustForConstraints(xy);
37625         }
37626         this.el.setXY(xy);
37627         this.el.show();
37628         this.hidden = false;
37629         this.focus();
37630         this.fireEvent("show", this);
37631     },
37632
37633     focus : function(){
37634         if(!this.hidden){
37635             this.doFocus.defer(50, this);
37636         }
37637     },
37638
37639     doFocus : function(){
37640         if(!this.hidden){
37641             this.focusEl.focus();
37642         }
37643     },
37644
37645     /**
37646      * Hides this menu and optionally all parent menus
37647      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37648      */
37649     hide : function(deep){
37650         if(this.el && this.isVisible()){
37651             this.fireEvent("beforehide", this);
37652             if(this.activeItem){
37653                 this.activeItem.deactivate();
37654                 this.activeItem = null;
37655             }
37656             this.el.hide();
37657             this.hidden = true;
37658             this.fireEvent("hide", this);
37659         }
37660         if(deep === true && this.parentMenu){
37661             this.parentMenu.hide(true);
37662         }
37663     },
37664
37665     /**
37666      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37667      * Any of the following are valid:
37668      * <ul>
37669      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37670      * <li>An HTMLElement object which will be converted to a menu item</li>
37671      * <li>A menu item config object that will be created as a new menu item</li>
37672      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37673      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37674      * </ul>
37675      * Usage:
37676      * <pre><code>
37677 // Create the menu
37678 var menu = new Roo.menu.Menu();
37679
37680 // Create a menu item to add by reference
37681 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37682
37683 // Add a bunch of items at once using different methods.
37684 // Only the last item added will be returned.
37685 var item = menu.add(
37686     menuItem,                // add existing item by ref
37687     'Dynamic Item',          // new TextItem
37688     '-',                     // new separator
37689     { text: 'Config Item' }  // new item by config
37690 );
37691 </code></pre>
37692      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37693      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37694      */
37695     add : function(){
37696         var a = arguments, l = a.length, item;
37697         for(var i = 0; i < l; i++){
37698             var el = a[i];
37699             if ((typeof(el) == "object") && el.xtype && el.xns) {
37700                 el = Roo.factory(el, Roo.menu);
37701             }
37702             
37703             if(el.render){ // some kind of Item
37704                 item = this.addItem(el);
37705             }else if(typeof el == "string"){ // string
37706                 if(el == "separator" || el == "-"){
37707                     item = this.addSeparator();
37708                 }else{
37709                     item = this.addText(el);
37710                 }
37711             }else if(el.tagName || el.el){ // element
37712                 item = this.addElement(el);
37713             }else if(typeof el == "object"){ // must be menu item config?
37714                 item = this.addMenuItem(el);
37715             }
37716         }
37717         return item;
37718     },
37719
37720     /**
37721      * Returns this menu's underlying {@link Roo.Element} object
37722      * @return {Roo.Element} The element
37723      */
37724     getEl : function(){
37725         if(!this.el){
37726             this.render();
37727         }
37728         return this.el;
37729     },
37730
37731     /**
37732      * Adds a separator bar to the menu
37733      * @return {Roo.menu.Item} The menu item that was added
37734      */
37735     addSeparator : function(){
37736         return this.addItem(new Roo.menu.Separator());
37737     },
37738
37739     /**
37740      * Adds an {@link Roo.Element} object to the menu
37741      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37742      * @return {Roo.menu.Item} The menu item that was added
37743      */
37744     addElement : function(el){
37745         return this.addItem(new Roo.menu.BaseItem(el));
37746     },
37747
37748     /**
37749      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37750      * @param {Roo.menu.Item} item The menu item to add
37751      * @return {Roo.menu.Item} The menu item that was added
37752      */
37753     addItem : function(item){
37754         this.items.add(item);
37755         if(this.ul){
37756             var li = document.createElement("li");
37757             li.className = "x-menu-list-item";
37758             this.ul.dom.appendChild(li);
37759             item.render(li, this);
37760             this.delayAutoWidth();
37761         }
37762         return item;
37763     },
37764
37765     /**
37766      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37767      * @param {Object} config A MenuItem config object
37768      * @return {Roo.menu.Item} The menu item that was added
37769      */
37770     addMenuItem : function(config){
37771         if(!(config instanceof Roo.menu.Item)){
37772             if(typeof config.checked == "boolean"){ // must be check menu item config?
37773                 config = new Roo.menu.CheckItem(config);
37774             }else{
37775                 config = new Roo.menu.Item(config);
37776             }
37777         }
37778         return this.addItem(config);
37779     },
37780
37781     /**
37782      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37783      * @param {String} text The text to display in the menu item
37784      * @return {Roo.menu.Item} The menu item that was added
37785      */
37786     addText : function(text){
37787         return this.addItem(new Roo.menu.TextItem({ text : text }));
37788     },
37789
37790     /**
37791      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37792      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37793      * @param {Roo.menu.Item} item The menu item to add
37794      * @return {Roo.menu.Item} The menu item that was added
37795      */
37796     insert : function(index, item){
37797         this.items.insert(index, item);
37798         if(this.ul){
37799             var li = document.createElement("li");
37800             li.className = "x-menu-list-item";
37801             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37802             item.render(li, this);
37803             this.delayAutoWidth();
37804         }
37805         return item;
37806     },
37807
37808     /**
37809      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37810      * @param {Roo.menu.Item} item The menu item to remove
37811      */
37812     remove : function(item){
37813         this.items.removeKey(item.id);
37814         item.destroy();
37815     },
37816
37817     /**
37818      * Removes and destroys all items in the menu
37819      */
37820     removeAll : function(){
37821         var f;
37822         while(f = this.items.first()){
37823             this.remove(f);
37824         }
37825     }
37826 });
37827
37828 // MenuNav is a private utility class used internally by the Menu
37829 Roo.menu.MenuNav = function(menu){
37830     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37831     this.scope = this.menu = menu;
37832 };
37833
37834 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37835     doRelay : function(e, h){
37836         var k = e.getKey();
37837         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37838             this.menu.tryActivate(0, 1);
37839             return false;
37840         }
37841         return h.call(this.scope || this, e, this.menu);
37842     },
37843
37844     up : function(e, m){
37845         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37846             m.tryActivate(m.items.length-1, -1);
37847         }
37848     },
37849
37850     down : function(e, m){
37851         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37852             m.tryActivate(0, 1);
37853         }
37854     },
37855
37856     right : function(e, m){
37857         if(m.activeItem){
37858             m.activeItem.expandMenu(true);
37859         }
37860     },
37861
37862     left : function(e, m){
37863         m.hide();
37864         if(m.parentMenu && m.parentMenu.activeItem){
37865             m.parentMenu.activeItem.activate();
37866         }
37867     },
37868
37869     enter : function(e, m){
37870         if(m.activeItem){
37871             e.stopPropagation();
37872             m.activeItem.onClick(e);
37873             m.fireEvent("click", this, m.activeItem);
37874             return true;
37875         }
37876     }
37877 });/*
37878  * Based on:
37879  * Ext JS Library 1.1.1
37880  * Copyright(c) 2006-2007, Ext JS, LLC.
37881  *
37882  * Originally Released Under LGPL - original licence link has changed is not relivant.
37883  *
37884  * Fork - LGPL
37885  * <script type="text/javascript">
37886  */
37887  
37888 /**
37889  * @class Roo.menu.MenuMgr
37890  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37891  * @singleton
37892  */
37893 Roo.menu.MenuMgr = function(){
37894    var menus, active, groups = {}, attached = false, lastShow = new Date();
37895
37896    // private - called when first menu is created
37897    function init(){
37898        menus = {};
37899        active = new Roo.util.MixedCollection();
37900        Roo.get(document).addKeyListener(27, function(){
37901            if(active.length > 0){
37902                hideAll();
37903            }
37904        });
37905    }
37906
37907    // private
37908    function hideAll(){
37909        if(active && active.length > 0){
37910            var c = active.clone();
37911            c.each(function(m){
37912                m.hide();
37913            });
37914        }
37915    }
37916
37917    // private
37918    function onHide(m){
37919        active.remove(m);
37920        if(active.length < 1){
37921            Roo.get(document).un("mousedown", onMouseDown);
37922            attached = false;
37923        }
37924    }
37925
37926    // private
37927    function onShow(m){
37928        var last = active.last();
37929        lastShow = new Date();
37930        active.add(m);
37931        if(!attached){
37932            Roo.get(document).on("mousedown", onMouseDown);
37933            attached = true;
37934        }
37935        if(m.parentMenu){
37936           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37937           m.parentMenu.activeChild = m;
37938        }else if(last && last.isVisible()){
37939           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37940        }
37941    }
37942
37943    // private
37944    function onBeforeHide(m){
37945        if(m.activeChild){
37946            m.activeChild.hide();
37947        }
37948        if(m.autoHideTimer){
37949            clearTimeout(m.autoHideTimer);
37950            delete m.autoHideTimer;
37951        }
37952    }
37953
37954    // private
37955    function onBeforeShow(m){
37956        var pm = m.parentMenu;
37957        if(!pm && !m.allowOtherMenus){
37958            hideAll();
37959        }else if(pm && pm.activeChild && active != m){
37960            pm.activeChild.hide();
37961        }
37962    }
37963
37964    // private
37965    function onMouseDown(e){
37966        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37967            hideAll();
37968        }
37969    }
37970
37971    // private
37972    function onBeforeCheck(mi, state){
37973        if(state){
37974            var g = groups[mi.group];
37975            for(var i = 0, l = g.length; i < l; i++){
37976                if(g[i] != mi){
37977                    g[i].setChecked(false);
37978                }
37979            }
37980        }
37981    }
37982
37983    return {
37984
37985        /**
37986         * Hides all menus that are currently visible
37987         */
37988        hideAll : function(){
37989             hideAll();  
37990        },
37991
37992        // private
37993        register : function(menu){
37994            if(!menus){
37995                init();
37996            }
37997            menus[menu.id] = menu;
37998            menu.on("beforehide", onBeforeHide);
37999            menu.on("hide", onHide);
38000            menu.on("beforeshow", onBeforeShow);
38001            menu.on("show", onShow);
38002            var g = menu.group;
38003            if(g && menu.events["checkchange"]){
38004                if(!groups[g]){
38005                    groups[g] = [];
38006                }
38007                groups[g].push(menu);
38008                menu.on("checkchange", onCheck);
38009            }
38010        },
38011
38012         /**
38013          * Returns a {@link Roo.menu.Menu} object
38014          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38015          * be used to generate and return a new Menu instance.
38016          */
38017        get : function(menu){
38018            if(typeof menu == "string"){ // menu id
38019                return menus[menu];
38020            }else if(menu.events){  // menu instance
38021                return menu;
38022            }else if(typeof menu.length == 'number'){ // array of menu items?
38023                return new Roo.menu.Menu({items:menu});
38024            }else{ // otherwise, must be a config
38025                return new Roo.menu.Menu(menu);
38026            }
38027        },
38028
38029        // private
38030        unregister : function(menu){
38031            delete menus[menu.id];
38032            menu.un("beforehide", onBeforeHide);
38033            menu.un("hide", onHide);
38034            menu.un("beforeshow", onBeforeShow);
38035            menu.un("show", onShow);
38036            var g = menu.group;
38037            if(g && menu.events["checkchange"]){
38038                groups[g].remove(menu);
38039                menu.un("checkchange", onCheck);
38040            }
38041        },
38042
38043        // private
38044        registerCheckable : function(menuItem){
38045            var g = menuItem.group;
38046            if(g){
38047                if(!groups[g]){
38048                    groups[g] = [];
38049                }
38050                groups[g].push(menuItem);
38051                menuItem.on("beforecheckchange", onBeforeCheck);
38052            }
38053        },
38054
38055        // private
38056        unregisterCheckable : function(menuItem){
38057            var g = menuItem.group;
38058            if(g){
38059                groups[g].remove(menuItem);
38060                menuItem.un("beforecheckchange", onBeforeCheck);
38061            }
38062        }
38063    };
38064 }();/*
38065  * Based on:
38066  * Ext JS Library 1.1.1
38067  * Copyright(c) 2006-2007, Ext JS, LLC.
38068  *
38069  * Originally Released Under LGPL - original licence link has changed is not relivant.
38070  *
38071  * Fork - LGPL
38072  * <script type="text/javascript">
38073  */
38074  
38075
38076 /**
38077  * @class Roo.menu.BaseItem
38078  * @extends Roo.Component
38079  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38080  * management and base configuration options shared by all menu components.
38081  * @constructor
38082  * Creates a new BaseItem
38083  * @param {Object} config Configuration options
38084  */
38085 Roo.menu.BaseItem = function(config){
38086     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38087
38088     this.addEvents({
38089         /**
38090          * @event click
38091          * Fires when this item is clicked
38092          * @param {Roo.menu.BaseItem} this
38093          * @param {Roo.EventObject} e
38094          */
38095         click: true,
38096         /**
38097          * @event activate
38098          * Fires when this item is activated
38099          * @param {Roo.menu.BaseItem} this
38100          */
38101         activate : true,
38102         /**
38103          * @event deactivate
38104          * Fires when this item is deactivated
38105          * @param {Roo.menu.BaseItem} this
38106          */
38107         deactivate : true
38108     });
38109
38110     if(this.handler){
38111         this.on("click", this.handler, this.scope, true);
38112     }
38113 };
38114
38115 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38116     /**
38117      * @cfg {Function} handler
38118      * A function that will handle the click event of this menu item (defaults to undefined)
38119      */
38120     /**
38121      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38122      */
38123     canActivate : false,
38124     
38125      /**
38126      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38127      */
38128     hidden: false,
38129     
38130     /**
38131      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38132      */
38133     activeClass : "x-menu-item-active",
38134     /**
38135      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38136      */
38137     hideOnClick : true,
38138     /**
38139      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38140      */
38141     hideDelay : 100,
38142
38143     // private
38144     ctype: "Roo.menu.BaseItem",
38145
38146     // private
38147     actionMode : "container",
38148
38149     // private
38150     render : function(container, parentMenu){
38151         this.parentMenu = parentMenu;
38152         Roo.menu.BaseItem.superclass.render.call(this, container);
38153         this.container.menuItemId = this.id;
38154     },
38155
38156     // private
38157     onRender : function(container, position){
38158         this.el = Roo.get(this.el);
38159         container.dom.appendChild(this.el.dom);
38160     },
38161
38162     // private
38163     onClick : function(e){
38164         if(!this.disabled && this.fireEvent("click", this, e) !== false
38165                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38166             this.handleClick(e);
38167         }else{
38168             e.stopEvent();
38169         }
38170     },
38171
38172     // private
38173     activate : function(){
38174         if(this.disabled){
38175             return false;
38176         }
38177         var li = this.container;
38178         li.addClass(this.activeClass);
38179         this.region = li.getRegion().adjust(2, 2, -2, -2);
38180         this.fireEvent("activate", this);
38181         return true;
38182     },
38183
38184     // private
38185     deactivate : function(){
38186         this.container.removeClass(this.activeClass);
38187         this.fireEvent("deactivate", this);
38188     },
38189
38190     // private
38191     shouldDeactivate : function(e){
38192         return !this.region || !this.region.contains(e.getPoint());
38193     },
38194
38195     // private
38196     handleClick : function(e){
38197         if(this.hideOnClick){
38198             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38199         }
38200     },
38201
38202     // private
38203     expandMenu : function(autoActivate){
38204         // do nothing
38205     },
38206
38207     // private
38208     hideMenu : function(){
38209         // do nothing
38210     }
38211 });/*
38212  * Based on:
38213  * Ext JS Library 1.1.1
38214  * Copyright(c) 2006-2007, Ext JS, LLC.
38215  *
38216  * Originally Released Under LGPL - original licence link has changed is not relivant.
38217  *
38218  * Fork - LGPL
38219  * <script type="text/javascript">
38220  */
38221  
38222 /**
38223  * @class Roo.menu.Adapter
38224  * @extends Roo.menu.BaseItem
38225  * 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.
38226  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38227  * @constructor
38228  * Creates a new Adapter
38229  * @param {Object} config Configuration options
38230  */
38231 Roo.menu.Adapter = function(component, config){
38232     Roo.menu.Adapter.superclass.constructor.call(this, config);
38233     this.component = component;
38234 };
38235 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38236     // private
38237     canActivate : true,
38238
38239     // private
38240     onRender : function(container, position){
38241         this.component.render(container);
38242         this.el = this.component.getEl();
38243     },
38244
38245     // private
38246     activate : function(){
38247         if(this.disabled){
38248             return false;
38249         }
38250         this.component.focus();
38251         this.fireEvent("activate", this);
38252         return true;
38253     },
38254
38255     // private
38256     deactivate : function(){
38257         this.fireEvent("deactivate", this);
38258     },
38259
38260     // private
38261     disable : function(){
38262         this.component.disable();
38263         Roo.menu.Adapter.superclass.disable.call(this);
38264     },
38265
38266     // private
38267     enable : function(){
38268         this.component.enable();
38269         Roo.menu.Adapter.superclass.enable.call(this);
38270     }
38271 });/*
38272  * Based on:
38273  * Ext JS Library 1.1.1
38274  * Copyright(c) 2006-2007, Ext JS, LLC.
38275  *
38276  * Originally Released Under LGPL - original licence link has changed is not relivant.
38277  *
38278  * Fork - LGPL
38279  * <script type="text/javascript">
38280  */
38281
38282 /**
38283  * @class Roo.menu.TextItem
38284  * @extends Roo.menu.BaseItem
38285  * Adds a static text string to a menu, usually used as either a heading or group separator.
38286  * Note: old style constructor with text is still supported.
38287  * 
38288  * @constructor
38289  * Creates a new TextItem
38290  * @param {Object} cfg Configuration
38291  */
38292 Roo.menu.TextItem = function(cfg){
38293     if (typeof(cfg) == 'string') {
38294         this.text = cfg;
38295     } else {
38296         Roo.apply(this,cfg);
38297     }
38298     
38299     Roo.menu.TextItem.superclass.constructor.call(this);
38300 };
38301
38302 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38303     /**
38304      * @cfg {Boolean} text Text to show on item.
38305      */
38306     text : '',
38307     
38308     /**
38309      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38310      */
38311     hideOnClick : false,
38312     /**
38313      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38314      */
38315     itemCls : "x-menu-text",
38316
38317     // private
38318     onRender : function(){
38319         var s = document.createElement("span");
38320         s.className = this.itemCls;
38321         s.innerHTML = this.text;
38322         this.el = s;
38323         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38324     }
38325 });/*
38326  * Based on:
38327  * Ext JS Library 1.1.1
38328  * Copyright(c) 2006-2007, Ext JS, LLC.
38329  *
38330  * Originally Released Under LGPL - original licence link has changed is not relivant.
38331  *
38332  * Fork - LGPL
38333  * <script type="text/javascript">
38334  */
38335
38336 /**
38337  * @class Roo.menu.Separator
38338  * @extends Roo.menu.BaseItem
38339  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38340  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38341  * @constructor
38342  * @param {Object} config Configuration options
38343  */
38344 Roo.menu.Separator = function(config){
38345     Roo.menu.Separator.superclass.constructor.call(this, config);
38346 };
38347
38348 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38349     /**
38350      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38351      */
38352     itemCls : "x-menu-sep",
38353     /**
38354      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38355      */
38356     hideOnClick : false,
38357
38358     // private
38359     onRender : function(li){
38360         var s = document.createElement("span");
38361         s.className = this.itemCls;
38362         s.innerHTML = "&#160;";
38363         this.el = s;
38364         li.addClass("x-menu-sep-li");
38365         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38366     }
38367 });/*
38368  * Based on:
38369  * Ext JS Library 1.1.1
38370  * Copyright(c) 2006-2007, Ext JS, LLC.
38371  *
38372  * Originally Released Under LGPL - original licence link has changed is not relivant.
38373  *
38374  * Fork - LGPL
38375  * <script type="text/javascript">
38376  */
38377 /**
38378  * @class Roo.menu.Item
38379  * @extends Roo.menu.BaseItem
38380  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38381  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38382  * activation and click handling.
38383  * @constructor
38384  * Creates a new Item
38385  * @param {Object} config Configuration options
38386  */
38387 Roo.menu.Item = function(config){
38388     Roo.menu.Item.superclass.constructor.call(this, config);
38389     if(this.menu){
38390         this.menu = Roo.menu.MenuMgr.get(this.menu);
38391     }
38392 };
38393 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38394     
38395     /**
38396      * @cfg {String} text
38397      * The text to show on the menu item.
38398      */
38399     text: '',
38400      /**
38401      * @cfg {String} HTML to render in menu
38402      * The text to show on the menu item (HTML version).
38403      */
38404     html: '',
38405     /**
38406      * @cfg {String} icon
38407      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38408      */
38409     icon: undefined,
38410     /**
38411      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38412      */
38413     itemCls : "x-menu-item",
38414     /**
38415      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38416      */
38417     canActivate : true,
38418     /**
38419      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38420      */
38421     showDelay: 200,
38422     // doc'd in BaseItem
38423     hideDelay: 200,
38424
38425     // private
38426     ctype: "Roo.menu.Item",
38427     
38428     // private
38429     onRender : function(container, position){
38430         var el = document.createElement("a");
38431         el.hideFocus = true;
38432         el.unselectable = "on";
38433         el.href = this.href || "#";
38434         if(this.hrefTarget){
38435             el.target = this.hrefTarget;
38436         }
38437         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38438         
38439         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38440         
38441         el.innerHTML = String.format(
38442                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38443                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38444         this.el = el;
38445         Roo.menu.Item.superclass.onRender.call(this, container, position);
38446     },
38447
38448     /**
38449      * Sets the text to display in this menu item
38450      * @param {String} text The text to display
38451      * @param {Boolean} isHTML true to indicate text is pure html.
38452      */
38453     setText : function(text, isHTML){
38454         if (isHTML) {
38455             this.html = text;
38456         } else {
38457             this.text = text;
38458             this.html = '';
38459         }
38460         if(this.rendered){
38461             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38462      
38463             this.el.update(String.format(
38464                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38465                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38466             this.parentMenu.autoWidth();
38467         }
38468     },
38469
38470     // private
38471     handleClick : function(e){
38472         if(!this.href){ // if no link defined, stop the event automatically
38473             e.stopEvent();
38474         }
38475         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38476     },
38477
38478     // private
38479     activate : function(autoExpand){
38480         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38481             this.focus();
38482             if(autoExpand){
38483                 this.expandMenu();
38484             }
38485         }
38486         return true;
38487     },
38488
38489     // private
38490     shouldDeactivate : function(e){
38491         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38492             if(this.menu && this.menu.isVisible()){
38493                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38494             }
38495             return true;
38496         }
38497         return false;
38498     },
38499
38500     // private
38501     deactivate : function(){
38502         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38503         this.hideMenu();
38504     },
38505
38506     // private
38507     expandMenu : function(autoActivate){
38508         if(!this.disabled && this.menu){
38509             clearTimeout(this.hideTimer);
38510             delete this.hideTimer;
38511             if(!this.menu.isVisible() && !this.showTimer){
38512                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38513             }else if (this.menu.isVisible() && autoActivate){
38514                 this.menu.tryActivate(0, 1);
38515             }
38516         }
38517     },
38518
38519     // private
38520     deferExpand : function(autoActivate){
38521         delete this.showTimer;
38522         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38523         if(autoActivate){
38524             this.menu.tryActivate(0, 1);
38525         }
38526     },
38527
38528     // private
38529     hideMenu : function(){
38530         clearTimeout(this.showTimer);
38531         delete this.showTimer;
38532         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38533             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38534         }
38535     },
38536
38537     // private
38538     deferHide : function(){
38539         delete this.hideTimer;
38540         this.menu.hide();
38541     }
38542 });/*
38543  * Based on:
38544  * Ext JS Library 1.1.1
38545  * Copyright(c) 2006-2007, Ext JS, LLC.
38546  *
38547  * Originally Released Under LGPL - original licence link has changed is not relivant.
38548  *
38549  * Fork - LGPL
38550  * <script type="text/javascript">
38551  */
38552  
38553 /**
38554  * @class Roo.menu.CheckItem
38555  * @extends Roo.menu.Item
38556  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38557  * @constructor
38558  * Creates a new CheckItem
38559  * @param {Object} config Configuration options
38560  */
38561 Roo.menu.CheckItem = function(config){
38562     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38563     this.addEvents({
38564         /**
38565          * @event beforecheckchange
38566          * Fires before the checked value is set, providing an opportunity to cancel if needed
38567          * @param {Roo.menu.CheckItem} this
38568          * @param {Boolean} checked The new checked value that will be set
38569          */
38570         "beforecheckchange" : true,
38571         /**
38572          * @event checkchange
38573          * Fires after the checked value has been set
38574          * @param {Roo.menu.CheckItem} this
38575          * @param {Boolean} checked The checked value that was set
38576          */
38577         "checkchange" : true
38578     });
38579     if(this.checkHandler){
38580         this.on('checkchange', this.checkHandler, this.scope);
38581     }
38582 };
38583 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38584     /**
38585      * @cfg {String} group
38586      * All check items with the same group name will automatically be grouped into a single-select
38587      * radio button group (defaults to '')
38588      */
38589     /**
38590      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38591      */
38592     itemCls : "x-menu-item x-menu-check-item",
38593     /**
38594      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38595      */
38596     groupClass : "x-menu-group-item",
38597
38598     /**
38599      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38600      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38601      * initialized with checked = true will be rendered as checked.
38602      */
38603     checked: false,
38604
38605     // private
38606     ctype: "Roo.menu.CheckItem",
38607
38608     // private
38609     onRender : function(c){
38610         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38611         if(this.group){
38612             this.el.addClass(this.groupClass);
38613         }
38614         Roo.menu.MenuMgr.registerCheckable(this);
38615         if(this.checked){
38616             this.checked = false;
38617             this.setChecked(true, true);
38618         }
38619     },
38620
38621     // private
38622     destroy : function(){
38623         if(this.rendered){
38624             Roo.menu.MenuMgr.unregisterCheckable(this);
38625         }
38626         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38627     },
38628
38629     /**
38630      * Set the checked state of this item
38631      * @param {Boolean} checked The new checked value
38632      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38633      */
38634     setChecked : function(state, suppressEvent){
38635         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38636             if(this.container){
38637                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38638             }
38639             this.checked = state;
38640             if(suppressEvent !== true){
38641                 this.fireEvent("checkchange", this, state);
38642             }
38643         }
38644     },
38645
38646     // private
38647     handleClick : function(e){
38648        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38649            this.setChecked(!this.checked);
38650        }
38651        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38652     }
38653 });/*
38654  * Based on:
38655  * Ext JS Library 1.1.1
38656  * Copyright(c) 2006-2007, Ext JS, LLC.
38657  *
38658  * Originally Released Under LGPL - original licence link has changed is not relivant.
38659  *
38660  * Fork - LGPL
38661  * <script type="text/javascript">
38662  */
38663  
38664 /**
38665  * @class Roo.menu.DateItem
38666  * @extends Roo.menu.Adapter
38667  * A menu item that wraps the {@link Roo.DatPicker} component.
38668  * @constructor
38669  * Creates a new DateItem
38670  * @param {Object} config Configuration options
38671  */
38672 Roo.menu.DateItem = function(config){
38673     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38674     /** The Roo.DatePicker object @type Roo.DatePicker */
38675     this.picker = this.component;
38676     this.addEvents({select: true});
38677     
38678     this.picker.on("render", function(picker){
38679         picker.getEl().swallowEvent("click");
38680         picker.container.addClass("x-menu-date-item");
38681     });
38682
38683     this.picker.on("select", this.onSelect, this);
38684 };
38685
38686 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38687     // private
38688     onSelect : function(picker, date){
38689         this.fireEvent("select", this, date, picker);
38690         Roo.menu.DateItem.superclass.handleClick.call(this);
38691     }
38692 });/*
38693  * Based on:
38694  * Ext JS Library 1.1.1
38695  * Copyright(c) 2006-2007, Ext JS, LLC.
38696  *
38697  * Originally Released Under LGPL - original licence link has changed is not relivant.
38698  *
38699  * Fork - LGPL
38700  * <script type="text/javascript">
38701  */
38702  
38703 /**
38704  * @class Roo.menu.ColorItem
38705  * @extends Roo.menu.Adapter
38706  * A menu item that wraps the {@link Roo.ColorPalette} component.
38707  * @constructor
38708  * Creates a new ColorItem
38709  * @param {Object} config Configuration options
38710  */
38711 Roo.menu.ColorItem = function(config){
38712     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38713     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38714     this.palette = this.component;
38715     this.relayEvents(this.palette, ["select"]);
38716     if(this.selectHandler){
38717         this.on('select', this.selectHandler, this.scope);
38718     }
38719 };
38720 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38721  * Based on:
38722  * Ext JS Library 1.1.1
38723  * Copyright(c) 2006-2007, Ext JS, LLC.
38724  *
38725  * Originally Released Under LGPL - original licence link has changed is not relivant.
38726  *
38727  * Fork - LGPL
38728  * <script type="text/javascript">
38729  */
38730  
38731
38732 /**
38733  * @class Roo.menu.DateMenu
38734  * @extends Roo.menu.Menu
38735  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38736  * @constructor
38737  * Creates a new DateMenu
38738  * @param {Object} config Configuration options
38739  */
38740 Roo.menu.DateMenu = function(config){
38741     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38742     this.plain = true;
38743     var di = new Roo.menu.DateItem(config);
38744     this.add(di);
38745     /**
38746      * The {@link Roo.DatePicker} instance for this DateMenu
38747      * @type DatePicker
38748      */
38749     this.picker = di.picker;
38750     /**
38751      * @event select
38752      * @param {DatePicker} picker
38753      * @param {Date} date
38754      */
38755     this.relayEvents(di, ["select"]);
38756     this.on('beforeshow', function(){
38757         if(this.picker){
38758             this.picker.hideMonthPicker(false);
38759         }
38760     }, this);
38761 };
38762 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38763     cls:'x-date-menu'
38764 });/*
38765  * Based on:
38766  * Ext JS Library 1.1.1
38767  * Copyright(c) 2006-2007, Ext JS, LLC.
38768  *
38769  * Originally Released Under LGPL - original licence link has changed is not relivant.
38770  *
38771  * Fork - LGPL
38772  * <script type="text/javascript">
38773  */
38774  
38775
38776 /**
38777  * @class Roo.menu.ColorMenu
38778  * @extends Roo.menu.Menu
38779  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38780  * @constructor
38781  * Creates a new ColorMenu
38782  * @param {Object} config Configuration options
38783  */
38784 Roo.menu.ColorMenu = function(config){
38785     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38786     this.plain = true;
38787     var ci = new Roo.menu.ColorItem(config);
38788     this.add(ci);
38789     /**
38790      * The {@link Roo.ColorPalette} instance for this ColorMenu
38791      * @type ColorPalette
38792      */
38793     this.palette = ci.palette;
38794     /**
38795      * @event select
38796      * @param {ColorPalette} palette
38797      * @param {String} color
38798      */
38799     this.relayEvents(ci, ["select"]);
38800 };
38801 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38802  * Based on:
38803  * Ext JS Library 1.1.1
38804  * Copyright(c) 2006-2007, Ext JS, LLC.
38805  *
38806  * Originally Released Under LGPL - original licence link has changed is not relivant.
38807  *
38808  * Fork - LGPL
38809  * <script type="text/javascript">
38810  */
38811  
38812 /**
38813  * @class Roo.form.TextItem
38814  * @extends Roo.BoxComponent
38815  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38816  * @constructor
38817  * Creates a new TextItem
38818  * @param {Object} config Configuration options
38819  */
38820 Roo.form.TextItem = function(config){
38821     Roo.form.TextItem.superclass.constructor.call(this, config);
38822 };
38823
38824 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38825     
38826     /**
38827      * @cfg {String} tag the tag for this item (default div)
38828      */
38829     tag : 'div',
38830     /**
38831      * @cfg {String} html the content for this item
38832      */
38833     html : '',
38834     
38835     getAutoCreate : function()
38836     {
38837         var cfg = {
38838             id: this.id,
38839             tag: this.tag,
38840             html: this.html,
38841             cls: 'x-form-item'
38842         };
38843         
38844         return cfg;
38845         
38846     },
38847     
38848     onRender : function(ct, position)
38849     {
38850         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38851         
38852         if(!this.el){
38853             var cfg = this.getAutoCreate();
38854             if(!cfg.name){
38855                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38856             }
38857             if (!cfg.name.length) {
38858                 delete cfg.name;
38859             }
38860             this.el = ct.createChild(cfg, position);
38861         }
38862     },
38863     /*
38864      * setHTML
38865      * @param {String} html update the Contents of the element.
38866      */
38867     setHTML : function(html)
38868     {
38869         this.fieldEl.dom.innerHTML = html;
38870     }
38871     
38872 });/*
38873  * Based on:
38874  * Ext JS Library 1.1.1
38875  * Copyright(c) 2006-2007, Ext JS, LLC.
38876  *
38877  * Originally Released Under LGPL - original licence link has changed is not relivant.
38878  *
38879  * Fork - LGPL
38880  * <script type="text/javascript">
38881  */
38882  
38883 /**
38884  * @class Roo.form.Field
38885  * @extends Roo.BoxComponent
38886  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38887  * @constructor
38888  * Creates a new Field
38889  * @param {Object} config Configuration options
38890  */
38891 Roo.form.Field = function(config){
38892     Roo.form.Field.superclass.constructor.call(this, config);
38893 };
38894
38895 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38896     /**
38897      * @cfg {String} fieldLabel Label to use when rendering a form.
38898      */
38899        /**
38900      * @cfg {String} qtip Mouse over tip
38901      */
38902      
38903     /**
38904      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38905      */
38906     invalidClass : "x-form-invalid",
38907     /**
38908      * @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")
38909      */
38910     invalidText : "The value in this field is invalid",
38911     /**
38912      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38913      */
38914     focusClass : "x-form-focus",
38915     /**
38916      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38917       automatic validation (defaults to "keyup").
38918      */
38919     validationEvent : "keyup",
38920     /**
38921      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38922      */
38923     validateOnBlur : true,
38924     /**
38925      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38926      */
38927     validationDelay : 250,
38928     /**
38929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38930      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38931      */
38932     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38933     /**
38934      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38935      */
38936     fieldClass : "x-form-field",
38937     /**
38938      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38939      *<pre>
38940 Value         Description
38941 -----------   ----------------------------------------------------------------------
38942 qtip          Display a quick tip when the user hovers over the field
38943 title         Display a default browser title attribute popup
38944 under         Add a block div beneath the field containing the error text
38945 side          Add an error icon to the right of the field with a popup on hover
38946 [element id]  Add the error text directly to the innerHTML of the specified element
38947 </pre>
38948      */
38949     msgTarget : 'qtip',
38950     /**
38951      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38952      */
38953     msgFx : 'normal',
38954
38955     /**
38956      * @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.
38957      */
38958     readOnly : false,
38959
38960     /**
38961      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38962      */
38963     disabled : false,
38964
38965     /**
38966      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38967      */
38968     inputType : undefined,
38969     
38970     /**
38971      * @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).
38972          */
38973         tabIndex : undefined,
38974         
38975     // private
38976     isFormField : true,
38977
38978     // private
38979     hasFocus : false,
38980     /**
38981      * @property {Roo.Element} fieldEl
38982      * Element Containing the rendered Field (with label etc.)
38983      */
38984     /**
38985      * @cfg {Mixed} value A value to initialize this field with.
38986      */
38987     value : undefined,
38988
38989     /**
38990      * @cfg {String} name The field's HTML name attribute.
38991      */
38992     /**
38993      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38994      */
38995     // private
38996     loadedValue : false,
38997      
38998      
38999         // private ??
39000         initComponent : function(){
39001         Roo.form.Field.superclass.initComponent.call(this);
39002         this.addEvents({
39003             /**
39004              * @event focus
39005              * Fires when this field receives input focus.
39006              * @param {Roo.form.Field} this
39007              */
39008             focus : true,
39009             /**
39010              * @event blur
39011              * Fires when this field loses input focus.
39012              * @param {Roo.form.Field} this
39013              */
39014             blur : true,
39015             /**
39016              * @event specialkey
39017              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39018              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39019              * @param {Roo.form.Field} this
39020              * @param {Roo.EventObject} e The event object
39021              */
39022             specialkey : true,
39023             /**
39024              * @event change
39025              * Fires just before the field blurs if the field value has changed.
39026              * @param {Roo.form.Field} this
39027              * @param {Mixed} newValue The new value
39028              * @param {Mixed} oldValue The original value
39029              */
39030             change : true,
39031             /**
39032              * @event invalid
39033              * Fires after the field has been marked as invalid.
39034              * @param {Roo.form.Field} this
39035              * @param {String} msg The validation message
39036              */
39037             invalid : true,
39038             /**
39039              * @event valid
39040              * Fires after the field has been validated with no errors.
39041              * @param {Roo.form.Field} this
39042              */
39043             valid : true,
39044              /**
39045              * @event keyup
39046              * Fires after the key up
39047              * @param {Roo.form.Field} this
39048              * @param {Roo.EventObject}  e The event Object
39049              */
39050             keyup : true
39051         });
39052     },
39053
39054     /**
39055      * Returns the name attribute of the field if available
39056      * @return {String} name The field name
39057      */
39058     getName: function(){
39059          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39060     },
39061
39062     // private
39063     onRender : function(ct, position){
39064         Roo.form.Field.superclass.onRender.call(this, ct, position);
39065         if(!this.el){
39066             var cfg = this.getAutoCreate();
39067             if(!cfg.name){
39068                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39069             }
39070             if (!cfg.name.length) {
39071                 delete cfg.name;
39072             }
39073             if(this.inputType){
39074                 cfg.type = this.inputType;
39075             }
39076             this.el = ct.createChild(cfg, position);
39077         }
39078         var type = this.el.dom.type;
39079         if(type){
39080             if(type == 'password'){
39081                 type = 'text';
39082             }
39083             this.el.addClass('x-form-'+type);
39084         }
39085         if(this.readOnly){
39086             this.el.dom.readOnly = true;
39087         }
39088         if(this.tabIndex !== undefined){
39089             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39090         }
39091
39092         this.el.addClass([this.fieldClass, this.cls]);
39093         this.initValue();
39094     },
39095
39096     /**
39097      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39098      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39099      * @return {Roo.form.Field} this
39100      */
39101     applyTo : function(target){
39102         this.allowDomMove = false;
39103         this.el = Roo.get(target);
39104         this.render(this.el.dom.parentNode);
39105         return this;
39106     },
39107
39108     // private
39109     initValue : function(){
39110         if(this.value !== undefined){
39111             this.setValue(this.value);
39112         }else if(this.el.dom.value.length > 0){
39113             this.setValue(this.el.dom.value);
39114         }
39115     },
39116
39117     /**
39118      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39119      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39120      */
39121     isDirty : function() {
39122         if(this.disabled) {
39123             return false;
39124         }
39125         return String(this.getValue()) !== String(this.originalValue);
39126     },
39127
39128     /**
39129      * stores the current value in loadedValue
39130      */
39131     resetHasChanged : function()
39132     {
39133         this.loadedValue = String(this.getValue());
39134     },
39135     /**
39136      * checks the current value against the 'loaded' value.
39137      * Note - will return false if 'resetHasChanged' has not been called first.
39138      */
39139     hasChanged : function()
39140     {
39141         if(this.disabled || this.readOnly) {
39142             return false;
39143         }
39144         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39145     },
39146     
39147     
39148     
39149     // private
39150     afterRender : function(){
39151         Roo.form.Field.superclass.afterRender.call(this);
39152         this.initEvents();
39153     },
39154
39155     // private
39156     fireKey : function(e){
39157         //Roo.log('field ' + e.getKey());
39158         if(e.isNavKeyPress()){
39159             this.fireEvent("specialkey", this, e);
39160         }
39161     },
39162
39163     /**
39164      * Resets the current field value to the originally loaded value and clears any validation messages
39165      */
39166     reset : function(){
39167         this.setValue(this.resetValue);
39168         this.originalValue = this.getValue();
39169         this.clearInvalid();
39170     },
39171
39172     // private
39173     initEvents : function(){
39174         // safari killled keypress - so keydown is now used..
39175         this.el.on("keydown" , this.fireKey,  this);
39176         this.el.on("focus", this.onFocus,  this);
39177         this.el.on("blur", this.onBlur,  this);
39178         this.el.relayEvent('keyup', this);
39179
39180         // reference to original value for reset
39181         this.originalValue = this.getValue();
39182         this.resetValue =  this.getValue();
39183     },
39184
39185     // private
39186     onFocus : function(){
39187         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39188             this.el.addClass(this.focusClass);
39189         }
39190         if(!this.hasFocus){
39191             this.hasFocus = true;
39192             this.startValue = this.getValue();
39193             this.fireEvent("focus", this);
39194         }
39195     },
39196
39197     beforeBlur : Roo.emptyFn,
39198
39199     // private
39200     onBlur : function(){
39201         this.beforeBlur();
39202         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39203             this.el.removeClass(this.focusClass);
39204         }
39205         this.hasFocus = false;
39206         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39207             this.validate();
39208         }
39209         var v = this.getValue();
39210         if(String(v) !== String(this.startValue)){
39211             this.fireEvent('change', this, v, this.startValue);
39212         }
39213         this.fireEvent("blur", this);
39214     },
39215
39216     /**
39217      * Returns whether or not the field value is currently valid
39218      * @param {Boolean} preventMark True to disable marking the field invalid
39219      * @return {Boolean} True if the value is valid, else false
39220      */
39221     isValid : function(preventMark){
39222         if(this.disabled){
39223             return true;
39224         }
39225         var restore = this.preventMark;
39226         this.preventMark = preventMark === true;
39227         var v = this.validateValue(this.processValue(this.getRawValue()));
39228         this.preventMark = restore;
39229         return v;
39230     },
39231
39232     /**
39233      * Validates the field value
39234      * @return {Boolean} True if the value is valid, else false
39235      */
39236     validate : function(){
39237         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39238             this.clearInvalid();
39239             return true;
39240         }
39241         return false;
39242     },
39243
39244     processValue : function(value){
39245         return value;
39246     },
39247
39248     // private
39249     // Subclasses should provide the validation implementation by overriding this
39250     validateValue : function(value){
39251         return true;
39252     },
39253
39254     /**
39255      * Mark this field as invalid
39256      * @param {String} msg The validation message
39257      */
39258     markInvalid : function(msg){
39259         if(!this.rendered || this.preventMark){ // not rendered
39260             return;
39261         }
39262         
39263         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39264         
39265         obj.el.addClass(this.invalidClass);
39266         msg = msg || this.invalidText;
39267         switch(this.msgTarget){
39268             case 'qtip':
39269                 obj.el.dom.qtip = msg;
39270                 obj.el.dom.qclass = 'x-form-invalid-tip';
39271                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39272                     Roo.QuickTips.enable();
39273                 }
39274                 break;
39275             case 'title':
39276                 this.el.dom.title = msg;
39277                 break;
39278             case 'under':
39279                 if(!this.errorEl){
39280                     var elp = this.el.findParent('.x-form-element', 5, true);
39281                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39282                     this.errorEl.setWidth(elp.getWidth(true)-20);
39283                 }
39284                 this.errorEl.update(msg);
39285                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39286                 break;
39287             case 'side':
39288                 if(!this.errorIcon){
39289                     var elp = this.el.findParent('.x-form-element', 5, true);
39290                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39291                 }
39292                 this.alignErrorIcon();
39293                 this.errorIcon.dom.qtip = msg;
39294                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39295                 this.errorIcon.show();
39296                 this.on('resize', this.alignErrorIcon, this);
39297                 break;
39298             default:
39299                 var t = Roo.getDom(this.msgTarget);
39300                 t.innerHTML = msg;
39301                 t.style.display = this.msgDisplay;
39302                 break;
39303         }
39304         this.fireEvent('invalid', this, msg);
39305     },
39306
39307     // private
39308     alignErrorIcon : function(){
39309         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39310     },
39311
39312     /**
39313      * Clear any invalid styles/messages for this field
39314      */
39315     clearInvalid : function(){
39316         if(!this.rendered || this.preventMark){ // not rendered
39317             return;
39318         }
39319         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39320         
39321         obj.el.removeClass(this.invalidClass);
39322         switch(this.msgTarget){
39323             case 'qtip':
39324                 obj.el.dom.qtip = '';
39325                 break;
39326             case 'title':
39327                 this.el.dom.title = '';
39328                 break;
39329             case 'under':
39330                 if(this.errorEl){
39331                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39332                 }
39333                 break;
39334             case 'side':
39335                 if(this.errorIcon){
39336                     this.errorIcon.dom.qtip = '';
39337                     this.errorIcon.hide();
39338                     this.un('resize', this.alignErrorIcon, this);
39339                 }
39340                 break;
39341             default:
39342                 var t = Roo.getDom(this.msgTarget);
39343                 t.innerHTML = '';
39344                 t.style.display = 'none';
39345                 break;
39346         }
39347         this.fireEvent('valid', this);
39348     },
39349
39350     /**
39351      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39352      * @return {Mixed} value The field value
39353      */
39354     getRawValue : function(){
39355         var v = this.el.getValue();
39356         
39357         return v;
39358     },
39359
39360     /**
39361      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39362      * @return {Mixed} value The field value
39363      */
39364     getValue : function(){
39365         var v = this.el.getValue();
39366          
39367         return v;
39368     },
39369
39370     /**
39371      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39372      * @param {Mixed} value The value to set
39373      */
39374     setRawValue : function(v){
39375         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39376     },
39377
39378     /**
39379      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39380      * @param {Mixed} value The value to set
39381      */
39382     setValue : function(v){
39383         this.value = v;
39384         if(this.rendered){
39385             this.el.dom.value = (v === null || v === undefined ? '' : v);
39386              this.validate();
39387         }
39388     },
39389
39390     adjustSize : function(w, h){
39391         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39392         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39393         return s;
39394     },
39395
39396     adjustWidth : function(tag, w){
39397         tag = tag.toLowerCase();
39398         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39399             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39400                 if(tag == 'input'){
39401                     return w + 2;
39402                 }
39403                 if(tag == 'textarea'){
39404                     return w-2;
39405                 }
39406             }else if(Roo.isOpera){
39407                 if(tag == 'input'){
39408                     return w + 2;
39409                 }
39410                 if(tag == 'textarea'){
39411                     return w-2;
39412                 }
39413             }
39414         }
39415         return w;
39416     }
39417 });
39418
39419
39420 // anything other than normal should be considered experimental
39421 Roo.form.Field.msgFx = {
39422     normal : {
39423         show: function(msgEl, f){
39424             msgEl.setDisplayed('block');
39425         },
39426
39427         hide : function(msgEl, f){
39428             msgEl.setDisplayed(false).update('');
39429         }
39430     },
39431
39432     slide : {
39433         show: function(msgEl, f){
39434             msgEl.slideIn('t', {stopFx:true});
39435         },
39436
39437         hide : function(msgEl, f){
39438             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39439         }
39440     },
39441
39442     slideRight : {
39443         show: function(msgEl, f){
39444             msgEl.fixDisplay();
39445             msgEl.alignTo(f.el, 'tl-tr');
39446             msgEl.slideIn('l', {stopFx:true});
39447         },
39448
39449         hide : function(msgEl, f){
39450             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39451         }
39452     }
39453 };/*
39454  * Based on:
39455  * Ext JS Library 1.1.1
39456  * Copyright(c) 2006-2007, Ext JS, LLC.
39457  *
39458  * Originally Released Under LGPL - original licence link has changed is not relivant.
39459  *
39460  * Fork - LGPL
39461  * <script type="text/javascript">
39462  */
39463  
39464
39465 /**
39466  * @class Roo.form.TextField
39467  * @extends Roo.form.Field
39468  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39469  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39470  * @constructor
39471  * Creates a new TextField
39472  * @param {Object} config Configuration options
39473  */
39474 Roo.form.TextField = function(config){
39475     Roo.form.TextField.superclass.constructor.call(this, config);
39476     this.addEvents({
39477         /**
39478          * @event autosize
39479          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39480          * according to the default logic, but this event provides a hook for the developer to apply additional
39481          * logic at runtime to resize the field if needed.
39482              * @param {Roo.form.Field} this This text field
39483              * @param {Number} width The new field width
39484              */
39485         autosize : true
39486     });
39487 };
39488
39489 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39490     /**
39491      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39492      */
39493     grow : false,
39494     /**
39495      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39496      */
39497     growMin : 30,
39498     /**
39499      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39500      */
39501     growMax : 800,
39502     /**
39503      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39504      */
39505     vtype : null,
39506     /**
39507      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39508      */
39509     maskRe : null,
39510     /**
39511      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39512      */
39513     disableKeyFilter : false,
39514     /**
39515      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39516      */
39517     allowBlank : true,
39518     /**
39519      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39520      */
39521     minLength : 0,
39522     /**
39523      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39524      */
39525     maxLength : Number.MAX_VALUE,
39526     /**
39527      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39528      */
39529     minLengthText : "The minimum length for this field is {0}",
39530     /**
39531      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39532      */
39533     maxLengthText : "The maximum length for this field is {0}",
39534     /**
39535      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39536      */
39537     selectOnFocus : false,
39538     /**
39539      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39540      */    
39541     allowLeadingSpace : false,
39542     /**
39543      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39544      */
39545     blankText : "This field is required",
39546     /**
39547      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39548      * If available, this function will be called only after the basic validators all return true, and will be passed the
39549      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39550      */
39551     validator : null,
39552     /**
39553      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39554      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39555      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39556      */
39557     regex : null,
39558     /**
39559      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39560      */
39561     regexText : "",
39562     /**
39563      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39564      */
39565     emptyText : null,
39566    
39567
39568     // private
39569     initEvents : function()
39570     {
39571         if (this.emptyText) {
39572             this.el.attr('placeholder', this.emptyText);
39573         }
39574         
39575         Roo.form.TextField.superclass.initEvents.call(this);
39576         if(this.validationEvent == 'keyup'){
39577             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39578             this.el.on('keyup', this.filterValidation, this);
39579         }
39580         else if(this.validationEvent !== false){
39581             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39582         }
39583         
39584         if(this.selectOnFocus){
39585             this.on("focus", this.preFocus, this);
39586         }
39587         if (!this.allowLeadingSpace) {
39588             this.on('blur', this.cleanLeadingSpace, this);
39589         }
39590         
39591         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39592             this.el.on("keypress", this.filterKeys, this);
39593         }
39594         if(this.grow){
39595             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39596             this.el.on("click", this.autoSize,  this);
39597         }
39598         if(this.el.is('input[type=password]') && Roo.isSafari){
39599             this.el.on('keydown', this.SafariOnKeyDown, this);
39600         }
39601     },
39602
39603     processValue : function(value){
39604         if(this.stripCharsRe){
39605             var newValue = value.replace(this.stripCharsRe, '');
39606             if(newValue !== value){
39607                 this.setRawValue(newValue);
39608                 return newValue;
39609             }
39610         }
39611         return value;
39612     },
39613
39614     filterValidation : function(e){
39615         if(!e.isNavKeyPress()){
39616             this.validationTask.delay(this.validationDelay);
39617         }
39618     },
39619
39620     // private
39621     onKeyUp : function(e){
39622         if(!e.isNavKeyPress()){
39623             this.autoSize();
39624         }
39625     },
39626     // private - clean the leading white space
39627     cleanLeadingSpace : function(e)
39628     {
39629         if ( this.inputType == 'file') {
39630             return;
39631         }
39632         
39633         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39634     },
39635     /**
39636      * Resets the current field value to the originally-loaded value and clears any validation messages.
39637      *  
39638      */
39639     reset : function(){
39640         Roo.form.TextField.superclass.reset.call(this);
39641        
39642     }, 
39643     // private
39644     preFocus : function(){
39645         
39646         if(this.selectOnFocus){
39647             this.el.dom.select();
39648         }
39649     },
39650
39651     
39652     // private
39653     filterKeys : function(e){
39654         var k = e.getKey();
39655         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39656             return;
39657         }
39658         var c = e.getCharCode(), cc = String.fromCharCode(c);
39659         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39660             return;
39661         }
39662         if(!this.maskRe.test(cc)){
39663             e.stopEvent();
39664         }
39665     },
39666
39667     setValue : function(v){
39668         
39669         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39670         
39671         this.autoSize();
39672     },
39673
39674     /**
39675      * Validates a value according to the field's validation rules and marks the field as invalid
39676      * if the validation fails
39677      * @param {Mixed} value The value to validate
39678      * @return {Boolean} True if the value is valid, else false
39679      */
39680     validateValue : function(value){
39681         if(value.length < 1)  { // if it's blank
39682              if(this.allowBlank){
39683                 this.clearInvalid();
39684                 return true;
39685              }else{
39686                 this.markInvalid(this.blankText);
39687                 return false;
39688              }
39689         }
39690         if(value.length < this.minLength){
39691             this.markInvalid(String.format(this.minLengthText, this.minLength));
39692             return false;
39693         }
39694         if(value.length > this.maxLength){
39695             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39696             return false;
39697         }
39698         if(this.vtype){
39699             var vt = Roo.form.VTypes;
39700             if(!vt[this.vtype](value, this)){
39701                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39702                 return false;
39703             }
39704         }
39705         if(typeof this.validator == "function"){
39706             var msg = this.validator(value);
39707             if(msg !== true){
39708                 this.markInvalid(msg);
39709                 return false;
39710             }
39711         }
39712         if(this.regex && !this.regex.test(value)){
39713             this.markInvalid(this.regexText);
39714             return false;
39715         }
39716         return true;
39717     },
39718
39719     /**
39720      * Selects text in this field
39721      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39722      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39723      */
39724     selectText : function(start, end){
39725         var v = this.getRawValue();
39726         if(v.length > 0){
39727             start = start === undefined ? 0 : start;
39728             end = end === undefined ? v.length : end;
39729             var d = this.el.dom;
39730             if(d.setSelectionRange){
39731                 d.setSelectionRange(start, end);
39732             }else if(d.createTextRange){
39733                 var range = d.createTextRange();
39734                 range.moveStart("character", start);
39735                 range.moveEnd("character", v.length-end);
39736                 range.select();
39737             }
39738         }
39739     },
39740
39741     /**
39742      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39743      * This only takes effect if grow = true, and fires the autosize event.
39744      */
39745     autoSize : function(){
39746         if(!this.grow || !this.rendered){
39747             return;
39748         }
39749         if(!this.metrics){
39750             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39751         }
39752         var el = this.el;
39753         var v = el.dom.value;
39754         var d = document.createElement('div');
39755         d.appendChild(document.createTextNode(v));
39756         v = d.innerHTML;
39757         d = null;
39758         v += "&#160;";
39759         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39760         this.el.setWidth(w);
39761         this.fireEvent("autosize", this, w);
39762     },
39763     
39764     // private
39765     SafariOnKeyDown : function(event)
39766     {
39767         // this is a workaround for a password hang bug on chrome/ webkit.
39768         
39769         var isSelectAll = false;
39770         
39771         if(this.el.dom.selectionEnd > 0){
39772             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39773         }
39774         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39775             event.preventDefault();
39776             this.setValue('');
39777             return;
39778         }
39779         
39780         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39781             
39782             event.preventDefault();
39783             // this is very hacky as keydown always get's upper case.
39784             
39785             var cc = String.fromCharCode(event.getCharCode());
39786             
39787             
39788             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39789             
39790         }
39791         
39792         
39793     }
39794 });/*
39795  * Based on:
39796  * Ext JS Library 1.1.1
39797  * Copyright(c) 2006-2007, Ext JS, LLC.
39798  *
39799  * Originally Released Under LGPL - original licence link has changed is not relivant.
39800  *
39801  * Fork - LGPL
39802  * <script type="text/javascript">
39803  */
39804  
39805 /**
39806  * @class Roo.form.Hidden
39807  * @extends Roo.form.TextField
39808  * Simple Hidden element used on forms 
39809  * 
39810  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39811  * 
39812  * @constructor
39813  * Creates a new Hidden form element.
39814  * @param {Object} config Configuration options
39815  */
39816
39817
39818
39819 // easy hidden field...
39820 Roo.form.Hidden = function(config){
39821     Roo.form.Hidden.superclass.constructor.call(this, config);
39822 };
39823   
39824 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39825     fieldLabel:      '',
39826     inputType:      'hidden',
39827     width:          50,
39828     allowBlank:     true,
39829     labelSeparator: '',
39830     hidden:         true,
39831     itemCls :       'x-form-item-display-none'
39832
39833
39834 });
39835
39836
39837 /*
39838  * Based on:
39839  * Ext JS Library 1.1.1
39840  * Copyright(c) 2006-2007, Ext JS, LLC.
39841  *
39842  * Originally Released Under LGPL - original licence link has changed is not relivant.
39843  *
39844  * Fork - LGPL
39845  * <script type="text/javascript">
39846  */
39847  
39848 /**
39849  * @class Roo.form.TriggerField
39850  * @extends Roo.form.TextField
39851  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39852  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39853  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39854  * for which you can provide a custom implementation.  For example:
39855  * <pre><code>
39856 var trigger = new Roo.form.TriggerField();
39857 trigger.onTriggerClick = myTriggerFn;
39858 trigger.applyTo('my-field');
39859 </code></pre>
39860  *
39861  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39862  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39863  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39864  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39865  * @constructor
39866  * Create a new TriggerField.
39867  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39868  * to the base TextField)
39869  */
39870 Roo.form.TriggerField = function(config){
39871     this.mimicing = false;
39872     Roo.form.TriggerField.superclass.constructor.call(this, config);
39873 };
39874
39875 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39876     /**
39877      * @cfg {String} triggerClass A CSS class to apply to the trigger
39878      */
39879     /**
39880      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39881      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39882      */
39883     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39884     /**
39885      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39886      */
39887     hideTrigger:false,
39888
39889     /** @cfg {Boolean} grow @hide */
39890     /** @cfg {Number} growMin @hide */
39891     /** @cfg {Number} growMax @hide */
39892
39893     /**
39894      * @hide 
39895      * @method
39896      */
39897     autoSize: Roo.emptyFn,
39898     // private
39899     monitorTab : true,
39900     // private
39901     deferHeight : true,
39902
39903     
39904     actionMode : 'wrap',
39905     // private
39906     onResize : function(w, h){
39907         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39908         if(typeof w == 'number'){
39909             var x = w - this.trigger.getWidth();
39910             this.el.setWidth(this.adjustWidth('input', x));
39911             this.trigger.setStyle('left', x+'px');
39912         }
39913     },
39914
39915     // private
39916     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39917
39918     // private
39919     getResizeEl : function(){
39920         return this.wrap;
39921     },
39922
39923     // private
39924     getPositionEl : function(){
39925         return this.wrap;
39926     },
39927
39928     // private
39929     alignErrorIcon : function(){
39930         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39931     },
39932
39933     // private
39934     onRender : function(ct, position){
39935         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39936         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39937         this.trigger = this.wrap.createChild(this.triggerConfig ||
39938                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39939         if(this.hideTrigger){
39940             this.trigger.setDisplayed(false);
39941         }
39942         this.initTrigger();
39943         if(!this.width){
39944             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39945         }
39946     },
39947
39948     // private
39949     initTrigger : function(){
39950         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39951         this.trigger.addClassOnOver('x-form-trigger-over');
39952         this.trigger.addClassOnClick('x-form-trigger-click');
39953     },
39954
39955     // private
39956     onDestroy : function(){
39957         if(this.trigger){
39958             this.trigger.removeAllListeners();
39959             this.trigger.remove();
39960         }
39961         if(this.wrap){
39962             this.wrap.remove();
39963         }
39964         Roo.form.TriggerField.superclass.onDestroy.call(this);
39965     },
39966
39967     // private
39968     onFocus : function(){
39969         Roo.form.TriggerField.superclass.onFocus.call(this);
39970         if(!this.mimicing){
39971             this.wrap.addClass('x-trigger-wrap-focus');
39972             this.mimicing = true;
39973             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39974             if(this.monitorTab){
39975                 this.el.on("keydown", this.checkTab, this);
39976             }
39977         }
39978     },
39979
39980     // private
39981     checkTab : function(e){
39982         if(e.getKey() == e.TAB){
39983             this.triggerBlur();
39984         }
39985     },
39986
39987     // private
39988     onBlur : function(){
39989         // do nothing
39990     },
39991
39992     // private
39993     mimicBlur : function(e, t){
39994         if(!this.wrap.contains(t) && this.validateBlur()){
39995             this.triggerBlur();
39996         }
39997     },
39998
39999     // private
40000     triggerBlur : function(){
40001         this.mimicing = false;
40002         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40003         if(this.monitorTab){
40004             this.el.un("keydown", this.checkTab, this);
40005         }
40006         this.wrap.removeClass('x-trigger-wrap-focus');
40007         Roo.form.TriggerField.superclass.onBlur.call(this);
40008     },
40009
40010     // private
40011     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40012     validateBlur : function(e, t){
40013         return true;
40014     },
40015
40016     // private
40017     onDisable : function(){
40018         Roo.form.TriggerField.superclass.onDisable.call(this);
40019         if(this.wrap){
40020             this.wrap.addClass('x-item-disabled');
40021         }
40022     },
40023
40024     // private
40025     onEnable : function(){
40026         Roo.form.TriggerField.superclass.onEnable.call(this);
40027         if(this.wrap){
40028             this.wrap.removeClass('x-item-disabled');
40029         }
40030     },
40031
40032     // private
40033     onShow : function(){
40034         var ae = this.getActionEl();
40035         
40036         if(ae){
40037             ae.dom.style.display = '';
40038             ae.dom.style.visibility = 'visible';
40039         }
40040     },
40041
40042     // private
40043     
40044     onHide : function(){
40045         var ae = this.getActionEl();
40046         ae.dom.style.display = 'none';
40047     },
40048
40049     /**
40050      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40051      * by an implementing function.
40052      * @method
40053      * @param {EventObject} e
40054      */
40055     onTriggerClick : Roo.emptyFn
40056 });
40057
40058 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40059 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40060 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40061 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40062     initComponent : function(){
40063         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40064
40065         this.triggerConfig = {
40066             tag:'span', cls:'x-form-twin-triggers', cn:[
40067             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40068             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40069         ]};
40070     },
40071
40072     getTrigger : function(index){
40073         return this.triggers[index];
40074     },
40075
40076     initTrigger : function(){
40077         var ts = this.trigger.select('.x-form-trigger', true);
40078         this.wrap.setStyle('overflow', 'hidden');
40079         var triggerField = this;
40080         ts.each(function(t, all, index){
40081             t.hide = function(){
40082                 var w = triggerField.wrap.getWidth();
40083                 this.dom.style.display = 'none';
40084                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40085             };
40086             t.show = function(){
40087                 var w = triggerField.wrap.getWidth();
40088                 this.dom.style.display = '';
40089                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40090             };
40091             var triggerIndex = 'Trigger'+(index+1);
40092
40093             if(this['hide'+triggerIndex]){
40094                 t.dom.style.display = 'none';
40095             }
40096             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40097             t.addClassOnOver('x-form-trigger-over');
40098             t.addClassOnClick('x-form-trigger-click');
40099         }, this);
40100         this.triggers = ts.elements;
40101     },
40102
40103     onTrigger1Click : Roo.emptyFn,
40104     onTrigger2Click : Roo.emptyFn
40105 });/*
40106  * Based on:
40107  * Ext JS Library 1.1.1
40108  * Copyright(c) 2006-2007, Ext JS, LLC.
40109  *
40110  * Originally Released Under LGPL - original licence link has changed is not relivant.
40111  *
40112  * Fork - LGPL
40113  * <script type="text/javascript">
40114  */
40115  
40116 /**
40117  * @class Roo.form.TextArea
40118  * @extends Roo.form.TextField
40119  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40120  * support for auto-sizing.
40121  * @constructor
40122  * Creates a new TextArea
40123  * @param {Object} config Configuration options
40124  */
40125 Roo.form.TextArea = function(config){
40126     Roo.form.TextArea.superclass.constructor.call(this, config);
40127     // these are provided exchanges for backwards compat
40128     // minHeight/maxHeight were replaced by growMin/growMax to be
40129     // compatible with TextField growing config values
40130     if(this.minHeight !== undefined){
40131         this.growMin = this.minHeight;
40132     }
40133     if(this.maxHeight !== undefined){
40134         this.growMax = this.maxHeight;
40135     }
40136 };
40137
40138 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40139     /**
40140      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40141      */
40142     growMin : 60,
40143     /**
40144      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40145      */
40146     growMax: 1000,
40147     /**
40148      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40149      * in the field (equivalent to setting overflow: hidden, defaults to false)
40150      */
40151     preventScrollbars: false,
40152     /**
40153      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40154      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40155      */
40156
40157     // private
40158     onRender : function(ct, position){
40159         if(!this.el){
40160             this.defaultAutoCreate = {
40161                 tag: "textarea",
40162                 style:"width:300px;height:60px;",
40163                 autocomplete: "new-password"
40164             };
40165         }
40166         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40167         if(this.grow){
40168             this.textSizeEl = Roo.DomHelper.append(document.body, {
40169                 tag: "pre", cls: "x-form-grow-sizer"
40170             });
40171             if(this.preventScrollbars){
40172                 this.el.setStyle("overflow", "hidden");
40173             }
40174             this.el.setHeight(this.growMin);
40175         }
40176     },
40177
40178     onDestroy : function(){
40179         if(this.textSizeEl){
40180             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40181         }
40182         Roo.form.TextArea.superclass.onDestroy.call(this);
40183     },
40184
40185     // private
40186     onKeyUp : function(e){
40187         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40188             this.autoSize();
40189         }
40190     },
40191
40192     /**
40193      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40194      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40195      */
40196     autoSize : function(){
40197         if(!this.grow || !this.textSizeEl){
40198             return;
40199         }
40200         var el = this.el;
40201         var v = el.dom.value;
40202         var ts = this.textSizeEl;
40203
40204         ts.innerHTML = '';
40205         ts.appendChild(document.createTextNode(v));
40206         v = ts.innerHTML;
40207
40208         Roo.fly(ts).setWidth(this.el.getWidth());
40209         if(v.length < 1){
40210             v = "&#160;&#160;";
40211         }else{
40212             if(Roo.isIE){
40213                 v = v.replace(/\n/g, '<p>&#160;</p>');
40214             }
40215             v += "&#160;\n&#160;";
40216         }
40217         ts.innerHTML = v;
40218         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40219         if(h != this.lastHeight){
40220             this.lastHeight = h;
40221             this.el.setHeight(h);
40222             this.fireEvent("autosize", this, h);
40223         }
40224     }
40225 });/*
40226  * Based on:
40227  * Ext JS Library 1.1.1
40228  * Copyright(c) 2006-2007, Ext JS, LLC.
40229  *
40230  * Originally Released Under LGPL - original licence link has changed is not relivant.
40231  *
40232  * Fork - LGPL
40233  * <script type="text/javascript">
40234  */
40235  
40236
40237 /**
40238  * @class Roo.form.NumberField
40239  * @extends Roo.form.TextField
40240  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40241  * @constructor
40242  * Creates a new NumberField
40243  * @param {Object} config Configuration options
40244  */
40245 Roo.form.NumberField = function(config){
40246     Roo.form.NumberField.superclass.constructor.call(this, config);
40247 };
40248
40249 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40250     /**
40251      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40252      */
40253     fieldClass: "x-form-field x-form-num-field",
40254     /**
40255      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40256      */
40257     allowDecimals : true,
40258     /**
40259      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40260      */
40261     decimalSeparator : ".",
40262     /**
40263      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40264      */
40265     decimalPrecision : 2,
40266     /**
40267      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40268      */
40269     allowNegative : true,
40270     /**
40271      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40272      */
40273     minValue : Number.NEGATIVE_INFINITY,
40274     /**
40275      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40276      */
40277     maxValue : Number.MAX_VALUE,
40278     /**
40279      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40280      */
40281     minText : "The minimum value for this field is {0}",
40282     /**
40283      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40284      */
40285     maxText : "The maximum value for this field is {0}",
40286     /**
40287      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40288      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40289      */
40290     nanText : "{0} is not a valid number",
40291
40292     // private
40293     initEvents : function(){
40294         Roo.form.NumberField.superclass.initEvents.call(this);
40295         var allowed = "0123456789";
40296         if(this.allowDecimals){
40297             allowed += this.decimalSeparator;
40298         }
40299         if(this.allowNegative){
40300             allowed += "-";
40301         }
40302         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40303         var keyPress = function(e){
40304             var k = e.getKey();
40305             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40306                 return;
40307             }
40308             var c = e.getCharCode();
40309             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40310                 e.stopEvent();
40311             }
40312         };
40313         this.el.on("keypress", keyPress, this);
40314     },
40315
40316     // private
40317     validateValue : function(value){
40318         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40319             return false;
40320         }
40321         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40322              return true;
40323         }
40324         var num = this.parseValue(value);
40325         if(isNaN(num)){
40326             this.markInvalid(String.format(this.nanText, value));
40327             return false;
40328         }
40329         if(num < this.minValue){
40330             this.markInvalid(String.format(this.minText, this.minValue));
40331             return false;
40332         }
40333         if(num > this.maxValue){
40334             this.markInvalid(String.format(this.maxText, this.maxValue));
40335             return false;
40336         }
40337         return true;
40338     },
40339
40340     getValue : function(){
40341         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40342     },
40343
40344     // private
40345     parseValue : function(value){
40346         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40347         return isNaN(value) ? '' : value;
40348     },
40349
40350     // private
40351     fixPrecision : function(value){
40352         var nan = isNaN(value);
40353         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40354             return nan ? '' : value;
40355         }
40356         return parseFloat(value).toFixed(this.decimalPrecision);
40357     },
40358
40359     setValue : function(v){
40360         v = this.fixPrecision(v);
40361         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40362     },
40363
40364     // private
40365     decimalPrecisionFcn : function(v){
40366         return Math.floor(v);
40367     },
40368
40369     beforeBlur : function(){
40370         var v = this.parseValue(this.getRawValue());
40371         if(v){
40372             this.setValue(v);
40373         }
40374     }
40375 });/*
40376  * Based on:
40377  * Ext JS Library 1.1.1
40378  * Copyright(c) 2006-2007, Ext JS, LLC.
40379  *
40380  * Originally Released Under LGPL - original licence link has changed is not relivant.
40381  *
40382  * Fork - LGPL
40383  * <script type="text/javascript">
40384  */
40385  
40386 /**
40387  * @class Roo.form.DateField
40388  * @extends Roo.form.TriggerField
40389  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40390 * @constructor
40391 * Create a new DateField
40392 * @param {Object} config
40393  */
40394 Roo.form.DateField = function(config)
40395 {
40396     Roo.form.DateField.superclass.constructor.call(this, config);
40397     
40398       this.addEvents({
40399          
40400         /**
40401          * @event select
40402          * Fires when a date is selected
40403              * @param {Roo.form.DateField} combo This combo box
40404              * @param {Date} date The date selected
40405              */
40406         'select' : true
40407          
40408     });
40409     
40410     
40411     if(typeof this.minValue == "string") {
40412         this.minValue = this.parseDate(this.minValue);
40413     }
40414     if(typeof this.maxValue == "string") {
40415         this.maxValue = this.parseDate(this.maxValue);
40416     }
40417     this.ddMatch = null;
40418     if(this.disabledDates){
40419         var dd = this.disabledDates;
40420         var re = "(?:";
40421         for(var i = 0; i < dd.length; i++){
40422             re += dd[i];
40423             if(i != dd.length-1) {
40424                 re += "|";
40425             }
40426         }
40427         this.ddMatch = new RegExp(re + ")");
40428     }
40429 };
40430
40431 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40432     /**
40433      * @cfg {String} format
40434      * The default date format string which can be overriden for localization support.  The format must be
40435      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40436      */
40437     format : "m/d/y",
40438     /**
40439      * @cfg {String} altFormats
40440      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40441      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40442      */
40443     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40444     /**
40445      * @cfg {Array} disabledDays
40446      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40447      */
40448     disabledDays : null,
40449     /**
40450      * @cfg {String} disabledDaysText
40451      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40452      */
40453     disabledDaysText : "Disabled",
40454     /**
40455      * @cfg {Array} disabledDates
40456      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40457      * expression so they are very powerful. Some examples:
40458      * <ul>
40459      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40460      * <li>["03/08", "09/16"] would disable those days for every year</li>
40461      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40462      * <li>["03/../2006"] would disable every day in March 2006</li>
40463      * <li>["^03"] would disable every day in every March</li>
40464      * </ul>
40465      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40466      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40467      */
40468     disabledDates : null,
40469     /**
40470      * @cfg {String} disabledDatesText
40471      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40472      */
40473     disabledDatesText : "Disabled",
40474     /**
40475      * @cfg {Date/String} minValue
40476      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40477      * valid format (defaults to null).
40478      */
40479     minValue : null,
40480     /**
40481      * @cfg {Date/String} maxValue
40482      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40483      * valid format (defaults to null).
40484      */
40485     maxValue : null,
40486     /**
40487      * @cfg {String} minText
40488      * The error text to display when the date in the cell is before minValue (defaults to
40489      * 'The date in this field must be after {minValue}').
40490      */
40491     minText : "The date in this field must be equal to or after {0}",
40492     /**
40493      * @cfg {String} maxText
40494      * The error text to display when the date in the cell is after maxValue (defaults to
40495      * 'The date in this field must be before {maxValue}').
40496      */
40497     maxText : "The date in this field must be equal to or before {0}",
40498     /**
40499      * @cfg {String} invalidText
40500      * The error text to display when the date in the field is invalid (defaults to
40501      * '{value} is not a valid date - it must be in the format {format}').
40502      */
40503     invalidText : "{0} is not a valid date - it must be in the format {1}",
40504     /**
40505      * @cfg {String} triggerClass
40506      * An additional CSS class used to style the trigger button.  The trigger will always get the
40507      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40508      * which displays a calendar icon).
40509      */
40510     triggerClass : 'x-form-date-trigger',
40511     
40512
40513     /**
40514      * @cfg {Boolean} useIso
40515      * if enabled, then the date field will use a hidden field to store the 
40516      * real value as iso formated date. default (false)
40517      */ 
40518     useIso : false,
40519     /**
40520      * @cfg {String/Object} autoCreate
40521      * A DomHelper element spec, or true for a default element spec (defaults to
40522      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40523      */ 
40524     // private
40525     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40526     
40527     // private
40528     hiddenField: false,
40529     
40530     onRender : function(ct, position)
40531     {
40532         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40533         if (this.useIso) {
40534             //this.el.dom.removeAttribute('name'); 
40535             Roo.log("Changing name?");
40536             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40537             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40538                     'before', true);
40539             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40540             // prevent input submission
40541             this.hiddenName = this.name;
40542         }
40543             
40544             
40545     },
40546     
40547     // private
40548     validateValue : function(value)
40549     {
40550         value = this.formatDate(value);
40551         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40552             Roo.log('super failed');
40553             return false;
40554         }
40555         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40556              return true;
40557         }
40558         var svalue = value;
40559         value = this.parseDate(value);
40560         if(!value){
40561             Roo.log('parse date failed' + svalue);
40562             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40563             return false;
40564         }
40565         var time = value.getTime();
40566         if(this.minValue && time < this.minValue.getTime()){
40567             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40568             return false;
40569         }
40570         if(this.maxValue && time > this.maxValue.getTime()){
40571             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40572             return false;
40573         }
40574         if(this.disabledDays){
40575             var day = value.getDay();
40576             for(var i = 0; i < this.disabledDays.length; i++) {
40577                 if(day === this.disabledDays[i]){
40578                     this.markInvalid(this.disabledDaysText);
40579                     return false;
40580                 }
40581             }
40582         }
40583         var fvalue = this.formatDate(value);
40584         if(this.ddMatch && this.ddMatch.test(fvalue)){
40585             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40586             return false;
40587         }
40588         return true;
40589     },
40590
40591     // private
40592     // Provides logic to override the default TriggerField.validateBlur which just returns true
40593     validateBlur : function(){
40594         return !this.menu || !this.menu.isVisible();
40595     },
40596     
40597     getName: function()
40598     {
40599         // returns hidden if it's set..
40600         if (!this.rendered) {return ''};
40601         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40602         
40603     },
40604
40605     /**
40606      * Returns the current date value of the date field.
40607      * @return {Date} The date value
40608      */
40609     getValue : function(){
40610         
40611         return  this.hiddenField ?
40612                 this.hiddenField.value :
40613                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40614     },
40615
40616     /**
40617      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40618      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40619      * (the default format used is "m/d/y").
40620      * <br />Usage:
40621      * <pre><code>
40622 //All of these calls set the same date value (May 4, 2006)
40623
40624 //Pass a date object:
40625 var dt = new Date('5/4/06');
40626 dateField.setValue(dt);
40627
40628 //Pass a date string (default format):
40629 dateField.setValue('5/4/06');
40630
40631 //Pass a date string (custom format):
40632 dateField.format = 'Y-m-d';
40633 dateField.setValue('2006-5-4');
40634 </code></pre>
40635      * @param {String/Date} date The date or valid date string
40636      */
40637     setValue : function(date){
40638         if (this.hiddenField) {
40639             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40640         }
40641         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40642         // make sure the value field is always stored as a date..
40643         this.value = this.parseDate(date);
40644         
40645         
40646     },
40647
40648     // private
40649     parseDate : function(value){
40650         if(!value || value instanceof Date){
40651             return value;
40652         }
40653         var v = Date.parseDate(value, this.format);
40654          if (!v && this.useIso) {
40655             v = Date.parseDate(value, 'Y-m-d');
40656         }
40657         if(!v && this.altFormats){
40658             if(!this.altFormatsArray){
40659                 this.altFormatsArray = this.altFormats.split("|");
40660             }
40661             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40662                 v = Date.parseDate(value, this.altFormatsArray[i]);
40663             }
40664         }
40665         return v;
40666     },
40667
40668     // private
40669     formatDate : function(date, fmt){
40670         return (!date || !(date instanceof Date)) ?
40671                date : date.dateFormat(fmt || this.format);
40672     },
40673
40674     // private
40675     menuListeners : {
40676         select: function(m, d){
40677             
40678             this.setValue(d);
40679             this.fireEvent('select', this, d);
40680         },
40681         show : function(){ // retain focus styling
40682             this.onFocus();
40683         },
40684         hide : function(){
40685             this.focus.defer(10, this);
40686             var ml = this.menuListeners;
40687             this.menu.un("select", ml.select,  this);
40688             this.menu.un("show", ml.show,  this);
40689             this.menu.un("hide", ml.hide,  this);
40690         }
40691     },
40692
40693     // private
40694     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40695     onTriggerClick : function(){
40696         if(this.disabled){
40697             return;
40698         }
40699         if(this.menu == null){
40700             this.menu = new Roo.menu.DateMenu();
40701         }
40702         Roo.apply(this.menu.picker,  {
40703             showClear: this.allowBlank,
40704             minDate : this.minValue,
40705             maxDate : this.maxValue,
40706             disabledDatesRE : this.ddMatch,
40707             disabledDatesText : this.disabledDatesText,
40708             disabledDays : this.disabledDays,
40709             disabledDaysText : this.disabledDaysText,
40710             format : this.useIso ? 'Y-m-d' : this.format,
40711             minText : String.format(this.minText, this.formatDate(this.minValue)),
40712             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40713         });
40714         this.menu.on(Roo.apply({}, this.menuListeners, {
40715             scope:this
40716         }));
40717         this.menu.picker.setValue(this.getValue() || new Date());
40718         this.menu.show(this.el, "tl-bl?");
40719     },
40720
40721     beforeBlur : function(){
40722         var v = this.parseDate(this.getRawValue());
40723         if(v){
40724             this.setValue(v);
40725         }
40726     },
40727
40728     /*@
40729      * overide
40730      * 
40731      */
40732     isDirty : function() {
40733         if(this.disabled) {
40734             return false;
40735         }
40736         
40737         if(typeof(this.startValue) === 'undefined'){
40738             return false;
40739         }
40740         
40741         return String(this.getValue()) !== String(this.startValue);
40742         
40743     },
40744     // @overide
40745     cleanLeadingSpace : function(e)
40746     {
40747        return;
40748     }
40749     
40750 });/*
40751  * Based on:
40752  * Ext JS Library 1.1.1
40753  * Copyright(c) 2006-2007, Ext JS, LLC.
40754  *
40755  * Originally Released Under LGPL - original licence link has changed is not relivant.
40756  *
40757  * Fork - LGPL
40758  * <script type="text/javascript">
40759  */
40760  
40761 /**
40762  * @class Roo.form.MonthField
40763  * @extends Roo.form.TriggerField
40764  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40765 * @constructor
40766 * Create a new MonthField
40767 * @param {Object} config
40768  */
40769 Roo.form.MonthField = function(config){
40770     
40771     Roo.form.MonthField.superclass.constructor.call(this, config);
40772     
40773       this.addEvents({
40774          
40775         /**
40776          * @event select
40777          * Fires when a date is selected
40778              * @param {Roo.form.MonthFieeld} combo This combo box
40779              * @param {Date} date The date selected
40780              */
40781         'select' : true
40782          
40783     });
40784     
40785     
40786     if(typeof this.minValue == "string") {
40787         this.minValue = this.parseDate(this.minValue);
40788     }
40789     if(typeof this.maxValue == "string") {
40790         this.maxValue = this.parseDate(this.maxValue);
40791     }
40792     this.ddMatch = null;
40793     if(this.disabledDates){
40794         var dd = this.disabledDates;
40795         var re = "(?:";
40796         for(var i = 0; i < dd.length; i++){
40797             re += dd[i];
40798             if(i != dd.length-1) {
40799                 re += "|";
40800             }
40801         }
40802         this.ddMatch = new RegExp(re + ")");
40803     }
40804 };
40805
40806 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40807     /**
40808      * @cfg {String} format
40809      * The default date format string which can be overriden for localization support.  The format must be
40810      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40811      */
40812     format : "M Y",
40813     /**
40814      * @cfg {String} altFormats
40815      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40816      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40817      */
40818     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40819     /**
40820      * @cfg {Array} disabledDays
40821      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40822      */
40823     disabledDays : [0,1,2,3,4,5,6],
40824     /**
40825      * @cfg {String} disabledDaysText
40826      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40827      */
40828     disabledDaysText : "Disabled",
40829     /**
40830      * @cfg {Array} disabledDates
40831      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40832      * expression so they are very powerful. Some examples:
40833      * <ul>
40834      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40835      * <li>["03/08", "09/16"] would disable those days for every year</li>
40836      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40837      * <li>["03/../2006"] would disable every day in March 2006</li>
40838      * <li>["^03"] would disable every day in every March</li>
40839      * </ul>
40840      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40841      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40842      */
40843     disabledDates : null,
40844     /**
40845      * @cfg {String} disabledDatesText
40846      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40847      */
40848     disabledDatesText : "Disabled",
40849     /**
40850      * @cfg {Date/String} minValue
40851      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40852      * valid format (defaults to null).
40853      */
40854     minValue : null,
40855     /**
40856      * @cfg {Date/String} maxValue
40857      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40858      * valid format (defaults to null).
40859      */
40860     maxValue : null,
40861     /**
40862      * @cfg {String} minText
40863      * The error text to display when the date in the cell is before minValue (defaults to
40864      * 'The date in this field must be after {minValue}').
40865      */
40866     minText : "The date in this field must be equal to or after {0}",
40867     /**
40868      * @cfg {String} maxTextf
40869      * The error text to display when the date in the cell is after maxValue (defaults to
40870      * 'The date in this field must be before {maxValue}').
40871      */
40872     maxText : "The date in this field must be equal to or before {0}",
40873     /**
40874      * @cfg {String} invalidText
40875      * The error text to display when the date in the field is invalid (defaults to
40876      * '{value} is not a valid date - it must be in the format {format}').
40877      */
40878     invalidText : "{0} is not a valid date - it must be in the format {1}",
40879     /**
40880      * @cfg {String} triggerClass
40881      * An additional CSS class used to style the trigger button.  The trigger will always get the
40882      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40883      * which displays a calendar icon).
40884      */
40885     triggerClass : 'x-form-date-trigger',
40886     
40887
40888     /**
40889      * @cfg {Boolean} useIso
40890      * if enabled, then the date field will use a hidden field to store the 
40891      * real value as iso formated date. default (true)
40892      */ 
40893     useIso : true,
40894     /**
40895      * @cfg {String/Object} autoCreate
40896      * A DomHelper element spec, or true for a default element spec (defaults to
40897      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40898      */ 
40899     // private
40900     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40901     
40902     // private
40903     hiddenField: false,
40904     
40905     hideMonthPicker : false,
40906     
40907     onRender : function(ct, position)
40908     {
40909         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40910         if (this.useIso) {
40911             this.el.dom.removeAttribute('name'); 
40912             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40913                     'before', true);
40914             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40915             // prevent input submission
40916             this.hiddenName = this.name;
40917         }
40918             
40919             
40920     },
40921     
40922     // private
40923     validateValue : function(value)
40924     {
40925         value = this.formatDate(value);
40926         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40927             return false;
40928         }
40929         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40930              return true;
40931         }
40932         var svalue = value;
40933         value = this.parseDate(value);
40934         if(!value){
40935             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40936             return false;
40937         }
40938         var time = value.getTime();
40939         if(this.minValue && time < this.minValue.getTime()){
40940             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40941             return false;
40942         }
40943         if(this.maxValue && time > this.maxValue.getTime()){
40944             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40945             return false;
40946         }
40947         /*if(this.disabledDays){
40948             var day = value.getDay();
40949             for(var i = 0; i < this.disabledDays.length; i++) {
40950                 if(day === this.disabledDays[i]){
40951                     this.markInvalid(this.disabledDaysText);
40952                     return false;
40953                 }
40954             }
40955         }
40956         */
40957         var fvalue = this.formatDate(value);
40958         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40959             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40960             return false;
40961         }
40962         */
40963         return true;
40964     },
40965
40966     // private
40967     // Provides logic to override the default TriggerField.validateBlur which just returns true
40968     validateBlur : function(){
40969         return !this.menu || !this.menu.isVisible();
40970     },
40971
40972     /**
40973      * Returns the current date value of the date field.
40974      * @return {Date} The date value
40975      */
40976     getValue : function(){
40977         
40978         
40979         
40980         return  this.hiddenField ?
40981                 this.hiddenField.value :
40982                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40983     },
40984
40985     /**
40986      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40987      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40988      * (the default format used is "m/d/y").
40989      * <br />Usage:
40990      * <pre><code>
40991 //All of these calls set the same date value (May 4, 2006)
40992
40993 //Pass a date object:
40994 var dt = new Date('5/4/06');
40995 monthField.setValue(dt);
40996
40997 //Pass a date string (default format):
40998 monthField.setValue('5/4/06');
40999
41000 //Pass a date string (custom format):
41001 monthField.format = 'Y-m-d';
41002 monthField.setValue('2006-5-4');
41003 </code></pre>
41004      * @param {String/Date} date The date or valid date string
41005      */
41006     setValue : function(date){
41007         Roo.log('month setValue' + date);
41008         // can only be first of month..
41009         
41010         var val = this.parseDate(date);
41011         
41012         if (this.hiddenField) {
41013             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41014         }
41015         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41016         this.value = this.parseDate(date);
41017     },
41018
41019     // private
41020     parseDate : function(value){
41021         if(!value || value instanceof Date){
41022             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41023             return value;
41024         }
41025         var v = Date.parseDate(value, this.format);
41026         if (!v && this.useIso) {
41027             v = Date.parseDate(value, 'Y-m-d');
41028         }
41029         if (v) {
41030             // 
41031             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41032         }
41033         
41034         
41035         if(!v && this.altFormats){
41036             if(!this.altFormatsArray){
41037                 this.altFormatsArray = this.altFormats.split("|");
41038             }
41039             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41040                 v = Date.parseDate(value, this.altFormatsArray[i]);
41041             }
41042         }
41043         return v;
41044     },
41045
41046     // private
41047     formatDate : function(date, fmt){
41048         return (!date || !(date instanceof Date)) ?
41049                date : date.dateFormat(fmt || this.format);
41050     },
41051
41052     // private
41053     menuListeners : {
41054         select: function(m, d){
41055             this.setValue(d);
41056             this.fireEvent('select', this, d);
41057         },
41058         show : function(){ // retain focus styling
41059             this.onFocus();
41060         },
41061         hide : function(){
41062             this.focus.defer(10, this);
41063             var ml = this.menuListeners;
41064             this.menu.un("select", ml.select,  this);
41065             this.menu.un("show", ml.show,  this);
41066             this.menu.un("hide", ml.hide,  this);
41067         }
41068     },
41069     // private
41070     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41071     onTriggerClick : function(){
41072         if(this.disabled){
41073             return;
41074         }
41075         if(this.menu == null){
41076             this.menu = new Roo.menu.DateMenu();
41077            
41078         }
41079         
41080         Roo.apply(this.menu.picker,  {
41081             
41082             showClear: this.allowBlank,
41083             minDate : this.minValue,
41084             maxDate : this.maxValue,
41085             disabledDatesRE : this.ddMatch,
41086             disabledDatesText : this.disabledDatesText,
41087             
41088             format : this.useIso ? 'Y-m-d' : this.format,
41089             minText : String.format(this.minText, this.formatDate(this.minValue)),
41090             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41091             
41092         });
41093          this.menu.on(Roo.apply({}, this.menuListeners, {
41094             scope:this
41095         }));
41096        
41097         
41098         var m = this.menu;
41099         var p = m.picker;
41100         
41101         // hide month picker get's called when we called by 'before hide';
41102         
41103         var ignorehide = true;
41104         p.hideMonthPicker  = function(disableAnim){
41105             if (ignorehide) {
41106                 return;
41107             }
41108              if(this.monthPicker){
41109                 Roo.log("hideMonthPicker called");
41110                 if(disableAnim === true){
41111                     this.monthPicker.hide();
41112                 }else{
41113                     this.monthPicker.slideOut('t', {duration:.2});
41114                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41115                     p.fireEvent("select", this, this.value);
41116                     m.hide();
41117                 }
41118             }
41119         }
41120         
41121         Roo.log('picker set value');
41122         Roo.log(this.getValue());
41123         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41124         m.show(this.el, 'tl-bl?');
41125         ignorehide  = false;
41126         // this will trigger hideMonthPicker..
41127         
41128         
41129         // hidden the day picker
41130         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41131         
41132         
41133         
41134       
41135         
41136         p.showMonthPicker.defer(100, p);
41137     
41138         
41139        
41140     },
41141
41142     beforeBlur : function(){
41143         var v = this.parseDate(this.getRawValue());
41144         if(v){
41145             this.setValue(v);
41146         }
41147     }
41148
41149     /** @cfg {Boolean} grow @hide */
41150     /** @cfg {Number} growMin @hide */
41151     /** @cfg {Number} growMax @hide */
41152     /**
41153      * @hide
41154      * @method autoSize
41155      */
41156 });/*
41157  * Based on:
41158  * Ext JS Library 1.1.1
41159  * Copyright(c) 2006-2007, Ext JS, LLC.
41160  *
41161  * Originally Released Under LGPL - original licence link has changed is not relivant.
41162  *
41163  * Fork - LGPL
41164  * <script type="text/javascript">
41165  */
41166  
41167
41168 /**
41169  * @class Roo.form.ComboBox
41170  * @extends Roo.form.TriggerField
41171  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41172  * @constructor
41173  * Create a new ComboBox.
41174  * @param {Object} config Configuration options
41175  */
41176 Roo.form.ComboBox = function(config){
41177     Roo.form.ComboBox.superclass.constructor.call(this, config);
41178     this.addEvents({
41179         /**
41180          * @event expand
41181          * Fires when the dropdown list is expanded
41182              * @param {Roo.form.ComboBox} combo This combo box
41183              */
41184         'expand' : true,
41185         /**
41186          * @event collapse
41187          * Fires when the dropdown list is collapsed
41188              * @param {Roo.form.ComboBox} combo This combo box
41189              */
41190         'collapse' : true,
41191         /**
41192          * @event beforeselect
41193          * Fires before a list item is selected. Return false to cancel the selection.
41194              * @param {Roo.form.ComboBox} combo This combo box
41195              * @param {Roo.data.Record} record The data record returned from the underlying store
41196              * @param {Number} index The index of the selected item in the dropdown list
41197              */
41198         'beforeselect' : true,
41199         /**
41200          * @event select
41201          * Fires when a list item is selected
41202              * @param {Roo.form.ComboBox} combo This combo box
41203              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41204              * @param {Number} index The index of the selected item in the dropdown list
41205              */
41206         'select' : true,
41207         /**
41208          * @event beforequery
41209          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41210          * The event object passed has these properties:
41211              * @param {Roo.form.ComboBox} combo This combo box
41212              * @param {String} query The query
41213              * @param {Boolean} forceAll true to force "all" query
41214              * @param {Boolean} cancel true to cancel the query
41215              * @param {Object} e The query event object
41216              */
41217         'beforequery': true,
41218          /**
41219          * @event add
41220          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41221              * @param {Roo.form.ComboBox} combo This combo box
41222              */
41223         'add' : true,
41224         /**
41225          * @event edit
41226          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41227              * @param {Roo.form.ComboBox} combo This combo box
41228              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41229              */
41230         'edit' : true
41231         
41232         
41233     });
41234     if(this.transform){
41235         this.allowDomMove = false;
41236         var s = Roo.getDom(this.transform);
41237         if(!this.hiddenName){
41238             this.hiddenName = s.name;
41239         }
41240         if(!this.store){
41241             this.mode = 'local';
41242             var d = [], opts = s.options;
41243             for(var i = 0, len = opts.length;i < len; i++){
41244                 var o = opts[i];
41245                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41246                 if(o.selected) {
41247                     this.value = value;
41248                 }
41249                 d.push([value, o.text]);
41250             }
41251             this.store = new Roo.data.SimpleStore({
41252                 'id': 0,
41253                 fields: ['value', 'text'],
41254                 data : d
41255             });
41256             this.valueField = 'value';
41257             this.displayField = 'text';
41258         }
41259         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41260         if(!this.lazyRender){
41261             this.target = true;
41262             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41263             s.parentNode.removeChild(s); // remove it
41264             this.render(this.el.parentNode);
41265         }else{
41266             s.parentNode.removeChild(s); // remove it
41267         }
41268
41269     }
41270     if (this.store) {
41271         this.store = Roo.factory(this.store, Roo.data);
41272     }
41273     
41274     this.selectedIndex = -1;
41275     if(this.mode == 'local'){
41276         if(config.queryDelay === undefined){
41277             this.queryDelay = 10;
41278         }
41279         if(config.minChars === undefined){
41280             this.minChars = 0;
41281         }
41282     }
41283 };
41284
41285 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41286     /**
41287      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41288      */
41289     /**
41290      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41291      * rendering into an Roo.Editor, defaults to false)
41292      */
41293     /**
41294      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41295      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41296      */
41297     /**
41298      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41299      */
41300     /**
41301      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41302      * the dropdown list (defaults to undefined, with no header element)
41303      */
41304
41305      /**
41306      * @cfg {String/Roo.Template} tpl The template to use to render the output
41307      */
41308      
41309     // private
41310     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41311     /**
41312      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41313      */
41314     listWidth: undefined,
41315     /**
41316      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41317      * mode = 'remote' or 'text' if mode = 'local')
41318      */
41319     displayField: undefined,
41320     /**
41321      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41322      * mode = 'remote' or 'value' if mode = 'local'). 
41323      * Note: use of a valueField requires the user make a selection
41324      * in order for a value to be mapped.
41325      */
41326     valueField: undefined,
41327     
41328     
41329     /**
41330      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41331      * field's data value (defaults to the underlying DOM element's name)
41332      */
41333     hiddenName: undefined,
41334     /**
41335      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41336      */
41337     listClass: '',
41338     /**
41339      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41340      */
41341     selectedClass: 'x-combo-selected',
41342     /**
41343      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41344      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41345      * which displays a downward arrow icon).
41346      */
41347     triggerClass : 'x-form-arrow-trigger',
41348     /**
41349      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41350      */
41351     shadow:'sides',
41352     /**
41353      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41354      * anchor positions (defaults to 'tl-bl')
41355      */
41356     listAlign: 'tl-bl?',
41357     /**
41358      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41359      */
41360     maxHeight: 300,
41361     /**
41362      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41363      * query specified by the allQuery config option (defaults to 'query')
41364      */
41365     triggerAction: 'query',
41366     /**
41367      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41368      * (defaults to 4, does not apply if editable = false)
41369      */
41370     minChars : 4,
41371     /**
41372      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41373      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41374      */
41375     typeAhead: false,
41376     /**
41377      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41378      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41379      */
41380     queryDelay: 500,
41381     /**
41382      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41383      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41384      */
41385     pageSize: 0,
41386     /**
41387      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41388      * when editable = true (defaults to false)
41389      */
41390     selectOnFocus:false,
41391     /**
41392      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41393      */
41394     queryParam: 'query',
41395     /**
41396      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41397      * when mode = 'remote' (defaults to 'Loading...')
41398      */
41399     loadingText: 'Loading...',
41400     /**
41401      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41402      */
41403     resizable: false,
41404     /**
41405      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41406      */
41407     handleHeight : 8,
41408     /**
41409      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41410      * traditional select (defaults to true)
41411      */
41412     editable: true,
41413     /**
41414      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41415      */
41416     allQuery: '',
41417     /**
41418      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41419      */
41420     mode: 'remote',
41421     /**
41422      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41423      * listWidth has a higher value)
41424      */
41425     minListWidth : 70,
41426     /**
41427      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41428      * allow the user to set arbitrary text into the field (defaults to false)
41429      */
41430     forceSelection:false,
41431     /**
41432      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41433      * if typeAhead = true (defaults to 250)
41434      */
41435     typeAheadDelay : 250,
41436     /**
41437      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41438      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41439      */
41440     valueNotFoundText : undefined,
41441     /**
41442      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41443      */
41444     blockFocus : false,
41445     
41446     /**
41447      * @cfg {Boolean} disableClear Disable showing of clear button.
41448      */
41449     disableClear : false,
41450     /**
41451      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41452      */
41453     alwaysQuery : false,
41454     
41455     //private
41456     addicon : false,
41457     editicon: false,
41458     
41459     // element that contains real text value.. (when hidden is used..)
41460      
41461     // private
41462     onRender : function(ct, position)
41463     {
41464         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41465         
41466         if(this.hiddenName){
41467             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41468                     'before', true);
41469             this.hiddenField.value =
41470                 this.hiddenValue !== undefined ? this.hiddenValue :
41471                 this.value !== undefined ? this.value : '';
41472
41473             // prevent input submission
41474             this.el.dom.removeAttribute('name');
41475              
41476              
41477         }
41478         
41479         if(Roo.isGecko){
41480             this.el.dom.setAttribute('autocomplete', 'off');
41481         }
41482
41483         var cls = 'x-combo-list';
41484
41485         this.list = new Roo.Layer({
41486             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41487         });
41488
41489         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41490         this.list.setWidth(lw);
41491         this.list.swallowEvent('mousewheel');
41492         this.assetHeight = 0;
41493
41494         if(this.title){
41495             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41496             this.assetHeight += this.header.getHeight();
41497         }
41498
41499         this.innerList = this.list.createChild({cls:cls+'-inner'});
41500         this.innerList.on('mouseover', this.onViewOver, this);
41501         this.innerList.on('mousemove', this.onViewMove, this);
41502         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41503         
41504         if(this.allowBlank && !this.pageSize && !this.disableClear){
41505             this.footer = this.list.createChild({cls:cls+'-ft'});
41506             this.pageTb = new Roo.Toolbar(this.footer);
41507            
41508         }
41509         if(this.pageSize){
41510             this.footer = this.list.createChild({cls:cls+'-ft'});
41511             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41512                     {pageSize: this.pageSize});
41513             
41514         }
41515         
41516         if (this.pageTb && this.allowBlank && !this.disableClear) {
41517             var _this = this;
41518             this.pageTb.add(new Roo.Toolbar.Fill(), {
41519                 cls: 'x-btn-icon x-btn-clear',
41520                 text: '&#160;',
41521                 handler: function()
41522                 {
41523                     _this.collapse();
41524                     _this.clearValue();
41525                     _this.onSelect(false, -1);
41526                 }
41527             });
41528         }
41529         if (this.footer) {
41530             this.assetHeight += this.footer.getHeight();
41531         }
41532         
41533
41534         if(!this.tpl){
41535             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41536         }
41537
41538         this.view = new Roo.View(this.innerList, this.tpl, {
41539             singleSelect:true,
41540             store: this.store,
41541             selectedClass: this.selectedClass
41542         });
41543
41544         this.view.on('click', this.onViewClick, this);
41545
41546         this.store.on('beforeload', this.onBeforeLoad, this);
41547         this.store.on('load', this.onLoad, this);
41548         this.store.on('loadexception', this.onLoadException, this);
41549
41550         if(this.resizable){
41551             this.resizer = new Roo.Resizable(this.list,  {
41552                pinned:true, handles:'se'
41553             });
41554             this.resizer.on('resize', function(r, w, h){
41555                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41556                 this.listWidth = w;
41557                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41558                 this.restrictHeight();
41559             }, this);
41560             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41561         }
41562         if(!this.editable){
41563             this.editable = true;
41564             this.setEditable(false);
41565         }  
41566         
41567         
41568         if (typeof(this.events.add.listeners) != 'undefined') {
41569             
41570             this.addicon = this.wrap.createChild(
41571                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41572        
41573             this.addicon.on('click', function(e) {
41574                 this.fireEvent('add', this);
41575             }, this);
41576         }
41577         if (typeof(this.events.edit.listeners) != 'undefined') {
41578             
41579             this.editicon = this.wrap.createChild(
41580                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41581             if (this.addicon) {
41582                 this.editicon.setStyle('margin-left', '40px');
41583             }
41584             this.editicon.on('click', function(e) {
41585                 
41586                 // we fire even  if inothing is selected..
41587                 this.fireEvent('edit', this, this.lastData );
41588                 
41589             }, this);
41590         }
41591         
41592         
41593         
41594     },
41595
41596     // private
41597     initEvents : function(){
41598         Roo.form.ComboBox.superclass.initEvents.call(this);
41599
41600         this.keyNav = new Roo.KeyNav(this.el, {
41601             "up" : function(e){
41602                 this.inKeyMode = true;
41603                 this.selectPrev();
41604             },
41605
41606             "down" : function(e){
41607                 if(!this.isExpanded()){
41608                     this.onTriggerClick();
41609                 }else{
41610                     this.inKeyMode = true;
41611                     this.selectNext();
41612                 }
41613             },
41614
41615             "enter" : function(e){
41616                 this.onViewClick();
41617                 //return true;
41618             },
41619
41620             "esc" : function(e){
41621                 this.collapse();
41622             },
41623
41624             "tab" : function(e){
41625                 this.onViewClick(false);
41626                 this.fireEvent("specialkey", this, e);
41627                 return true;
41628             },
41629
41630             scope : this,
41631
41632             doRelay : function(foo, bar, hname){
41633                 if(hname == 'down' || this.scope.isExpanded()){
41634                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41635                 }
41636                 return true;
41637             },
41638
41639             forceKeyDown: true
41640         });
41641         this.queryDelay = Math.max(this.queryDelay || 10,
41642                 this.mode == 'local' ? 10 : 250);
41643         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41644         if(this.typeAhead){
41645             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41646         }
41647         if(this.editable !== false){
41648             this.el.on("keyup", this.onKeyUp, this);
41649         }
41650         if(this.forceSelection){
41651             this.on('blur', this.doForce, this);
41652         }
41653     },
41654
41655     onDestroy : function(){
41656         if(this.view){
41657             this.view.setStore(null);
41658             this.view.el.removeAllListeners();
41659             this.view.el.remove();
41660             this.view.purgeListeners();
41661         }
41662         if(this.list){
41663             this.list.destroy();
41664         }
41665         if(this.store){
41666             this.store.un('beforeload', this.onBeforeLoad, this);
41667             this.store.un('load', this.onLoad, this);
41668             this.store.un('loadexception', this.onLoadException, this);
41669         }
41670         Roo.form.ComboBox.superclass.onDestroy.call(this);
41671     },
41672
41673     // private
41674     fireKey : function(e){
41675         if(e.isNavKeyPress() && !this.list.isVisible()){
41676             this.fireEvent("specialkey", this, e);
41677         }
41678     },
41679
41680     // private
41681     onResize: function(w, h){
41682         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41683         
41684         if(typeof w != 'number'){
41685             // we do not handle it!?!?
41686             return;
41687         }
41688         var tw = this.trigger.getWidth();
41689         tw += this.addicon ? this.addicon.getWidth() : 0;
41690         tw += this.editicon ? this.editicon.getWidth() : 0;
41691         var x = w - tw;
41692         this.el.setWidth( this.adjustWidth('input', x));
41693             
41694         this.trigger.setStyle('left', x+'px');
41695         
41696         if(this.list && this.listWidth === undefined){
41697             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41698             this.list.setWidth(lw);
41699             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41700         }
41701         
41702     
41703         
41704     },
41705
41706     /**
41707      * Allow or prevent the user from directly editing the field text.  If false is passed,
41708      * the user will only be able to select from the items defined in the dropdown list.  This method
41709      * is the runtime equivalent of setting the 'editable' config option at config time.
41710      * @param {Boolean} value True to allow the user to directly edit the field text
41711      */
41712     setEditable : function(value){
41713         if(value == this.editable){
41714             return;
41715         }
41716         this.editable = value;
41717         if(!value){
41718             this.el.dom.setAttribute('readOnly', true);
41719             this.el.on('mousedown', this.onTriggerClick,  this);
41720             this.el.addClass('x-combo-noedit');
41721         }else{
41722             this.el.dom.setAttribute('readOnly', false);
41723             this.el.un('mousedown', this.onTriggerClick,  this);
41724             this.el.removeClass('x-combo-noedit');
41725         }
41726     },
41727
41728     // private
41729     onBeforeLoad : function(){
41730         if(!this.hasFocus){
41731             return;
41732         }
41733         this.innerList.update(this.loadingText ?
41734                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41735         this.restrictHeight();
41736         this.selectedIndex = -1;
41737     },
41738
41739     // private
41740     onLoad : function(){
41741         if(!this.hasFocus){
41742             return;
41743         }
41744         if(this.store.getCount() > 0){
41745             this.expand();
41746             this.restrictHeight();
41747             if(this.lastQuery == this.allQuery){
41748                 if(this.editable){
41749                     this.el.dom.select();
41750                 }
41751                 if(!this.selectByValue(this.value, true)){
41752                     this.select(0, true);
41753                 }
41754             }else{
41755                 this.selectNext();
41756                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41757                     this.taTask.delay(this.typeAheadDelay);
41758                 }
41759             }
41760         }else{
41761             this.onEmptyResults();
41762         }
41763         //this.el.focus();
41764     },
41765     // private
41766     onLoadException : function()
41767     {
41768         this.collapse();
41769         Roo.log(this.store.reader.jsonData);
41770         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41771             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41772         }
41773         
41774         
41775     },
41776     // private
41777     onTypeAhead : function(){
41778         if(this.store.getCount() > 0){
41779             var r = this.store.getAt(0);
41780             var newValue = r.data[this.displayField];
41781             var len = newValue.length;
41782             var selStart = this.getRawValue().length;
41783             if(selStart != len){
41784                 this.setRawValue(newValue);
41785                 this.selectText(selStart, newValue.length);
41786             }
41787         }
41788     },
41789
41790     // private
41791     onSelect : function(record, index){
41792         if(this.fireEvent('beforeselect', this, record, index) !== false){
41793             this.setFromData(index > -1 ? record.data : false);
41794             this.collapse();
41795             this.fireEvent('select', this, record, index);
41796         }
41797     },
41798
41799     /**
41800      * Returns the currently selected field value or empty string if no value is set.
41801      * @return {String} value The selected value
41802      */
41803     getValue : function(){
41804         if(this.valueField){
41805             return typeof this.value != 'undefined' ? this.value : '';
41806         }
41807         return Roo.form.ComboBox.superclass.getValue.call(this);
41808     },
41809
41810     /**
41811      * Clears any text/value currently set in the field
41812      */
41813     clearValue : function(){
41814         if(this.hiddenField){
41815             this.hiddenField.value = '';
41816         }
41817         this.value = '';
41818         this.setRawValue('');
41819         this.lastSelectionText = '';
41820         
41821     },
41822
41823     /**
41824      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41825      * will be displayed in the field.  If the value does not match the data value of an existing item,
41826      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41827      * Otherwise the field will be blank (although the value will still be set).
41828      * @param {String} value The value to match
41829      */
41830     setValue : function(v){
41831         var text = v;
41832         if(this.valueField){
41833             var r = this.findRecord(this.valueField, v);
41834             if(r){
41835                 text = r.data[this.displayField];
41836             }else if(this.valueNotFoundText !== undefined){
41837                 text = this.valueNotFoundText;
41838             }
41839         }
41840         this.lastSelectionText = text;
41841         if(this.hiddenField){
41842             this.hiddenField.value = v;
41843         }
41844         Roo.form.ComboBox.superclass.setValue.call(this, text);
41845         this.value = v;
41846     },
41847     /**
41848      * @property {Object} the last set data for the element
41849      */
41850     
41851     lastData : false,
41852     /**
41853      * Sets the value of the field based on a object which is related to the record format for the store.
41854      * @param {Object} value the value to set as. or false on reset?
41855      */
41856     setFromData : function(o){
41857         var dv = ''; // display value
41858         var vv = ''; // value value..
41859         this.lastData = o;
41860         if (this.displayField) {
41861             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41862         } else {
41863             // this is an error condition!!!
41864             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41865         }
41866         
41867         if(this.valueField){
41868             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41869         }
41870         if(this.hiddenField){
41871             this.hiddenField.value = vv;
41872             
41873             this.lastSelectionText = dv;
41874             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41875             this.value = vv;
41876             return;
41877         }
41878         // no hidden field.. - we store the value in 'value', but still display
41879         // display field!!!!
41880         this.lastSelectionText = dv;
41881         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41882         this.value = vv;
41883         
41884         
41885     },
41886     // private
41887     reset : function(){
41888         // overridden so that last data is reset..
41889         this.setValue(this.resetValue);
41890         this.originalValue = this.getValue();
41891         this.clearInvalid();
41892         this.lastData = false;
41893         if (this.view) {
41894             this.view.clearSelections();
41895         }
41896     },
41897     // private
41898     findRecord : function(prop, value){
41899         var record;
41900         if(this.store.getCount() > 0){
41901             this.store.each(function(r){
41902                 if(r.data[prop] == value){
41903                     record = r;
41904                     return false;
41905                 }
41906                 return true;
41907             });
41908         }
41909         return record;
41910     },
41911     
41912     getName: function()
41913     {
41914         // returns hidden if it's set..
41915         if (!this.rendered) {return ''};
41916         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41917         
41918     },
41919     // private
41920     onViewMove : function(e, t){
41921         this.inKeyMode = false;
41922     },
41923
41924     // private
41925     onViewOver : function(e, t){
41926         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41927             return;
41928         }
41929         var item = this.view.findItemFromChild(t);
41930         if(item){
41931             var index = this.view.indexOf(item);
41932             this.select(index, false);
41933         }
41934     },
41935
41936     // private
41937     onViewClick : function(doFocus)
41938     {
41939         var index = this.view.getSelectedIndexes()[0];
41940         var r = this.store.getAt(index);
41941         if(r){
41942             this.onSelect(r, index);
41943         }
41944         if(doFocus !== false && !this.blockFocus){
41945             this.el.focus();
41946         }
41947     },
41948
41949     // private
41950     restrictHeight : function(){
41951         this.innerList.dom.style.height = '';
41952         var inner = this.innerList.dom;
41953         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41954         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41955         this.list.beginUpdate();
41956         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41957         this.list.alignTo(this.el, this.listAlign);
41958         this.list.endUpdate();
41959     },
41960
41961     // private
41962     onEmptyResults : function(){
41963         this.collapse();
41964     },
41965
41966     /**
41967      * Returns true if the dropdown list is expanded, else false.
41968      */
41969     isExpanded : function(){
41970         return this.list.isVisible();
41971     },
41972
41973     /**
41974      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41975      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41976      * @param {String} value The data value of the item to select
41977      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41978      * selected item if it is not currently in view (defaults to true)
41979      * @return {Boolean} True if the value matched an item in the list, else false
41980      */
41981     selectByValue : function(v, scrollIntoView){
41982         if(v !== undefined && v !== null){
41983             var r = this.findRecord(this.valueField || this.displayField, v);
41984             if(r){
41985                 this.select(this.store.indexOf(r), scrollIntoView);
41986                 return true;
41987             }
41988         }
41989         return false;
41990     },
41991
41992     /**
41993      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41995      * @param {Number} index The zero-based index of the list item to select
41996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41997      * selected item if it is not currently in view (defaults to true)
41998      */
41999     select : function(index, scrollIntoView){
42000         this.selectedIndex = index;
42001         this.view.select(index);
42002         if(scrollIntoView !== false){
42003             var el = this.view.getNode(index);
42004             if(el){
42005                 this.innerList.scrollChildIntoView(el, false);
42006             }
42007         }
42008     },
42009
42010     // private
42011     selectNext : function(){
42012         var ct = this.store.getCount();
42013         if(ct > 0){
42014             if(this.selectedIndex == -1){
42015                 this.select(0);
42016             }else if(this.selectedIndex < ct-1){
42017                 this.select(this.selectedIndex+1);
42018             }
42019         }
42020     },
42021
42022     // private
42023     selectPrev : function(){
42024         var ct = this.store.getCount();
42025         if(ct > 0){
42026             if(this.selectedIndex == -1){
42027                 this.select(0);
42028             }else if(this.selectedIndex != 0){
42029                 this.select(this.selectedIndex-1);
42030             }
42031         }
42032     },
42033
42034     // private
42035     onKeyUp : function(e){
42036         if(this.editable !== false && !e.isSpecialKey()){
42037             this.lastKey = e.getKey();
42038             this.dqTask.delay(this.queryDelay);
42039         }
42040     },
42041
42042     // private
42043     validateBlur : function(){
42044         return !this.list || !this.list.isVisible();   
42045     },
42046
42047     // private
42048     initQuery : function(){
42049         this.doQuery(this.getRawValue());
42050     },
42051
42052     // private
42053     doForce : function(){
42054         if(this.el.dom.value.length > 0){
42055             this.el.dom.value =
42056                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42057              
42058         }
42059     },
42060
42061     /**
42062      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42063      * query allowing the query action to be canceled if needed.
42064      * @param {String} query The SQL query to execute
42065      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42066      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42067      * saved in the current store (defaults to false)
42068      */
42069     doQuery : function(q, forceAll){
42070         if(q === undefined || q === null){
42071             q = '';
42072         }
42073         var qe = {
42074             query: q,
42075             forceAll: forceAll,
42076             combo: this,
42077             cancel:false
42078         };
42079         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42080             return false;
42081         }
42082         q = qe.query;
42083         forceAll = qe.forceAll;
42084         if(forceAll === true || (q.length >= this.minChars)){
42085             if(this.lastQuery != q || this.alwaysQuery){
42086                 this.lastQuery = q;
42087                 if(this.mode == 'local'){
42088                     this.selectedIndex = -1;
42089                     if(forceAll){
42090                         this.store.clearFilter();
42091                     }else{
42092                         this.store.filter(this.displayField, q);
42093                     }
42094                     this.onLoad();
42095                 }else{
42096                     this.store.baseParams[this.queryParam] = q;
42097                     this.store.load({
42098                         params: this.getParams(q)
42099                     });
42100                     this.expand();
42101                 }
42102             }else{
42103                 this.selectedIndex = -1;
42104                 this.onLoad();   
42105             }
42106         }
42107     },
42108
42109     // private
42110     getParams : function(q){
42111         var p = {};
42112         //p[this.queryParam] = q;
42113         if(this.pageSize){
42114             p.start = 0;
42115             p.limit = this.pageSize;
42116         }
42117         return p;
42118     },
42119
42120     /**
42121      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42122      */
42123     collapse : function(){
42124         if(!this.isExpanded()){
42125             return;
42126         }
42127         this.list.hide();
42128         Roo.get(document).un('mousedown', this.collapseIf, this);
42129         Roo.get(document).un('mousewheel', this.collapseIf, this);
42130         if (!this.editable) {
42131             Roo.get(document).un('keydown', this.listKeyPress, this);
42132         }
42133         this.fireEvent('collapse', this);
42134     },
42135
42136     // private
42137     collapseIf : function(e){
42138         if(!e.within(this.wrap) && !e.within(this.list)){
42139             this.collapse();
42140         }
42141     },
42142
42143     /**
42144      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42145      */
42146     expand : function(){
42147         if(this.isExpanded() || !this.hasFocus){
42148             return;
42149         }
42150         this.list.alignTo(this.el, this.listAlign);
42151         this.list.show();
42152         Roo.get(document).on('mousedown', this.collapseIf, this);
42153         Roo.get(document).on('mousewheel', this.collapseIf, this);
42154         if (!this.editable) {
42155             Roo.get(document).on('keydown', this.listKeyPress, this);
42156         }
42157         
42158         this.fireEvent('expand', this);
42159     },
42160
42161     // private
42162     // Implements the default empty TriggerField.onTriggerClick function
42163     onTriggerClick : function(){
42164         if(this.disabled){
42165             return;
42166         }
42167         if(this.isExpanded()){
42168             this.collapse();
42169             if (!this.blockFocus) {
42170                 this.el.focus();
42171             }
42172             
42173         }else {
42174             this.hasFocus = true;
42175             if(this.triggerAction == 'all') {
42176                 this.doQuery(this.allQuery, true);
42177             } else {
42178                 this.doQuery(this.getRawValue());
42179             }
42180             if (!this.blockFocus) {
42181                 this.el.focus();
42182             }
42183         }
42184     },
42185     listKeyPress : function(e)
42186     {
42187         //Roo.log('listkeypress');
42188         // scroll to first matching element based on key pres..
42189         if (e.isSpecialKey()) {
42190             return false;
42191         }
42192         var k = String.fromCharCode(e.getKey()).toUpperCase();
42193         //Roo.log(k);
42194         var match  = false;
42195         var csel = this.view.getSelectedNodes();
42196         var cselitem = false;
42197         if (csel.length) {
42198             var ix = this.view.indexOf(csel[0]);
42199             cselitem  = this.store.getAt(ix);
42200             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42201                 cselitem = false;
42202             }
42203             
42204         }
42205         
42206         this.store.each(function(v) { 
42207             if (cselitem) {
42208                 // start at existing selection.
42209                 if (cselitem.id == v.id) {
42210                     cselitem = false;
42211                 }
42212                 return;
42213             }
42214                 
42215             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42216                 match = this.store.indexOf(v);
42217                 return false;
42218             }
42219         }, this);
42220         
42221         if (match === false) {
42222             return true; // no more action?
42223         }
42224         // scroll to?
42225         this.view.select(match);
42226         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42227         sn.scrollIntoView(sn.dom.parentNode, false);
42228     } 
42229
42230     /** 
42231     * @cfg {Boolean} grow 
42232     * @hide 
42233     */
42234     /** 
42235     * @cfg {Number} growMin 
42236     * @hide 
42237     */
42238     /** 
42239     * @cfg {Number} growMax 
42240     * @hide 
42241     */
42242     /**
42243      * @hide
42244      * @method autoSize
42245      */
42246 });/*
42247  * Copyright(c) 2010-2012, Roo J Solutions Limited
42248  *
42249  * Licence LGPL
42250  *
42251  */
42252
42253 /**
42254  * @class Roo.form.ComboBoxArray
42255  * @extends Roo.form.TextField
42256  * A facebook style adder... for lists of email / people / countries  etc...
42257  * pick multiple items from a combo box, and shows each one.
42258  *
42259  *  Fred [x]  Brian [x]  [Pick another |v]
42260  *
42261  *
42262  *  For this to work: it needs various extra information
42263  *    - normal combo problay has
42264  *      name, hiddenName
42265  *    + displayField, valueField
42266  *
42267  *    For our purpose...
42268  *
42269  *
42270  *   If we change from 'extends' to wrapping...
42271  *   
42272  *  
42273  *
42274  
42275  
42276  * @constructor
42277  * Create a new ComboBoxArray.
42278  * @param {Object} config Configuration options
42279  */
42280  
42281
42282 Roo.form.ComboBoxArray = function(config)
42283 {
42284     this.addEvents({
42285         /**
42286          * @event beforeremove
42287          * Fires before remove the value from the list
42288              * @param {Roo.form.ComboBoxArray} _self This combo box array
42289              * @param {Roo.form.ComboBoxArray.Item} item removed item
42290              */
42291         'beforeremove' : true,
42292         /**
42293          * @event remove
42294          * Fires when remove the value from the list
42295              * @param {Roo.form.ComboBoxArray} _self This combo box array
42296              * @param {Roo.form.ComboBoxArray.Item} item removed item
42297              */
42298         'remove' : true
42299         
42300         
42301     });
42302     
42303     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42304     
42305     this.items = new Roo.util.MixedCollection(false);
42306     
42307     // construct the child combo...
42308     
42309     
42310     
42311     
42312    
42313     
42314 }
42315
42316  
42317 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42318
42319     /**
42320      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42321      */
42322     
42323     lastData : false,
42324     
42325     // behavies liek a hiddne field
42326     inputType:      'hidden',
42327     /**
42328      * @cfg {Number} width The width of the box that displays the selected element
42329      */ 
42330     width:          300,
42331
42332     
42333     
42334     /**
42335      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42336      */
42337     name : false,
42338     /**
42339      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42340      */
42341     hiddenName : false,
42342       /**
42343      * @cfg {String} seperator    The value seperator normally ',' 
42344      */
42345     seperator : ',',
42346     
42347     // private the array of items that are displayed..
42348     items  : false,
42349     // private - the hidden field el.
42350     hiddenEl : false,
42351     // private - the filed el..
42352     el : false,
42353     
42354     //validateValue : function() { return true; }, // all values are ok!
42355     //onAddClick: function() { },
42356     
42357     onRender : function(ct, position) 
42358     {
42359         
42360         // create the standard hidden element
42361         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42362         
42363         
42364         // give fake names to child combo;
42365         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42366         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42367         
42368         this.combo = Roo.factory(this.combo, Roo.form);
42369         this.combo.onRender(ct, position);
42370         if (typeof(this.combo.width) != 'undefined') {
42371             this.combo.onResize(this.combo.width,0);
42372         }
42373         
42374         this.combo.initEvents();
42375         
42376         // assigned so form know we need to do this..
42377         this.store          = this.combo.store;
42378         this.valueField     = this.combo.valueField;
42379         this.displayField   = this.combo.displayField ;
42380         
42381         
42382         this.combo.wrap.addClass('x-cbarray-grp');
42383         
42384         var cbwrap = this.combo.wrap.createChild(
42385             {tag: 'div', cls: 'x-cbarray-cb'},
42386             this.combo.el.dom
42387         );
42388         
42389              
42390         this.hiddenEl = this.combo.wrap.createChild({
42391             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42392         });
42393         this.el = this.combo.wrap.createChild({
42394             tag: 'input',  type:'hidden' , name: this.name, value : ''
42395         });
42396          //   this.el.dom.removeAttribute("name");
42397         
42398         
42399         this.outerWrap = this.combo.wrap;
42400         this.wrap = cbwrap;
42401         
42402         this.outerWrap.setWidth(this.width);
42403         this.outerWrap.dom.removeChild(this.el.dom);
42404         
42405         this.wrap.dom.appendChild(this.el.dom);
42406         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42407         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42408         
42409         this.combo.trigger.setStyle('position','relative');
42410         this.combo.trigger.setStyle('left', '0px');
42411         this.combo.trigger.setStyle('top', '2px');
42412         
42413         this.combo.el.setStyle('vertical-align', 'text-bottom');
42414         
42415         //this.trigger.setStyle('vertical-align', 'top');
42416         
42417         // this should use the code from combo really... on('add' ....)
42418         if (this.adder) {
42419             
42420         
42421             this.adder = this.outerWrap.createChild(
42422                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42423             var _t = this;
42424             this.adder.on('click', function(e) {
42425                 _t.fireEvent('adderclick', this, e);
42426             }, _t);
42427         }
42428         //var _t = this;
42429         //this.adder.on('click', this.onAddClick, _t);
42430         
42431         
42432         this.combo.on('select', function(cb, rec, ix) {
42433             this.addItem(rec.data);
42434             
42435             cb.setValue('');
42436             cb.el.dom.value = '';
42437             //cb.lastData = rec.data;
42438             // add to list
42439             
42440         }, this);
42441         
42442         
42443     },
42444     
42445     
42446     getName: function()
42447     {
42448         // returns hidden if it's set..
42449         if (!this.rendered) {return ''};
42450         return  this.hiddenName ? this.hiddenName : this.name;
42451         
42452     },
42453     
42454     
42455     onResize: function(w, h){
42456         
42457         return;
42458         // not sure if this is needed..
42459         //this.combo.onResize(w,h);
42460         
42461         if(typeof w != 'number'){
42462             // we do not handle it!?!?
42463             return;
42464         }
42465         var tw = this.combo.trigger.getWidth();
42466         tw += this.addicon ? this.addicon.getWidth() : 0;
42467         tw += this.editicon ? this.editicon.getWidth() : 0;
42468         var x = w - tw;
42469         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42470             
42471         this.combo.trigger.setStyle('left', '0px');
42472         
42473         if(this.list && this.listWidth === undefined){
42474             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42475             this.list.setWidth(lw);
42476             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42477         }
42478         
42479     
42480         
42481     },
42482     
42483     addItem: function(rec)
42484     {
42485         var valueField = this.combo.valueField;
42486         var displayField = this.combo.displayField;
42487         
42488         if (this.items.indexOfKey(rec[valueField]) > -1) {
42489             //console.log("GOT " + rec.data.id);
42490             return;
42491         }
42492         
42493         var x = new Roo.form.ComboBoxArray.Item({
42494             //id : rec[this.idField],
42495             data : rec,
42496             displayField : displayField ,
42497             tipField : displayField ,
42498             cb : this
42499         });
42500         // use the 
42501         this.items.add(rec[valueField],x);
42502         // add it before the element..
42503         this.updateHiddenEl();
42504         x.render(this.outerWrap, this.wrap.dom);
42505         // add the image handler..
42506     },
42507     
42508     updateHiddenEl : function()
42509     {
42510         this.validate();
42511         if (!this.hiddenEl) {
42512             return;
42513         }
42514         var ar = [];
42515         var idField = this.combo.valueField;
42516         
42517         this.items.each(function(f) {
42518             ar.push(f.data[idField]);
42519         });
42520         this.hiddenEl.dom.value = ar.join(this.seperator);
42521         this.validate();
42522     },
42523     
42524     reset : function()
42525     {
42526         this.items.clear();
42527         
42528         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42529            el.remove();
42530         });
42531         
42532         this.el.dom.value = '';
42533         if (this.hiddenEl) {
42534             this.hiddenEl.dom.value = '';
42535         }
42536         
42537     },
42538     getValue: function()
42539     {
42540         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42541     },
42542     setValue: function(v) // not a valid action - must use addItems..
42543     {
42544         
42545         this.reset();
42546          
42547         if (this.store.isLocal && (typeof(v) == 'string')) {
42548             // then we can use the store to find the values..
42549             // comma seperated at present.. this needs to allow JSON based encoding..
42550             this.hiddenEl.value  = v;
42551             var v_ar = [];
42552             Roo.each(v.split(this.seperator), function(k) {
42553                 Roo.log("CHECK " + this.valueField + ',' + k);
42554                 var li = this.store.query(this.valueField, k);
42555                 if (!li.length) {
42556                     return;
42557                 }
42558                 var add = {};
42559                 add[this.valueField] = k;
42560                 add[this.displayField] = li.item(0).data[this.displayField];
42561                 
42562                 this.addItem(add);
42563             }, this) 
42564              
42565         }
42566         if (typeof(v) == 'object' ) {
42567             // then let's assume it's an array of objects..
42568             Roo.each(v, function(l) {
42569                 var add = l;
42570                 if (typeof(l) == 'string') {
42571                     add = {};
42572                     add[this.valueField] = l;
42573                     add[this.displayField] = l
42574                 }
42575                 this.addItem(add);
42576             }, this);
42577              
42578         }
42579         
42580         
42581     },
42582     setFromData: function(v)
42583     {
42584         // this recieves an object, if setValues is called.
42585         this.reset();
42586         this.el.dom.value = v[this.displayField];
42587         this.hiddenEl.dom.value = v[this.valueField];
42588         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42589             return;
42590         }
42591         var kv = v[this.valueField];
42592         var dv = v[this.displayField];
42593         kv = typeof(kv) != 'string' ? '' : kv;
42594         dv = typeof(dv) != 'string' ? '' : dv;
42595         
42596         
42597         var keys = kv.split(this.seperator);
42598         var display = dv.split(this.seperator);
42599         for (var i = 0 ; i < keys.length; i++) {
42600             add = {};
42601             add[this.valueField] = keys[i];
42602             add[this.displayField] = display[i];
42603             this.addItem(add);
42604         }
42605       
42606         
42607     },
42608     
42609     /**
42610      * Validates the combox array value
42611      * @return {Boolean} True if the value is valid, else false
42612      */
42613     validate : function(){
42614         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42615             this.clearInvalid();
42616             return true;
42617         }
42618         return false;
42619     },
42620     
42621     validateValue : function(value){
42622         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42623         
42624     },
42625     
42626     /*@
42627      * overide
42628      * 
42629      */
42630     isDirty : function() {
42631         if(this.disabled) {
42632             return false;
42633         }
42634         
42635         try {
42636             var d = Roo.decode(String(this.originalValue));
42637         } catch (e) {
42638             return String(this.getValue()) !== String(this.originalValue);
42639         }
42640         
42641         var originalValue = [];
42642         
42643         for (var i = 0; i < d.length; i++){
42644             originalValue.push(d[i][this.valueField]);
42645         }
42646         
42647         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42648         
42649     }
42650     
42651 });
42652
42653
42654
42655 /**
42656  * @class Roo.form.ComboBoxArray.Item
42657  * @extends Roo.BoxComponent
42658  * A selected item in the list
42659  *  Fred [x]  Brian [x]  [Pick another |v]
42660  * 
42661  * @constructor
42662  * Create a new item.
42663  * @param {Object} config Configuration options
42664  */
42665  
42666 Roo.form.ComboBoxArray.Item = function(config) {
42667     config.id = Roo.id();
42668     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42669 }
42670
42671 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42672     data : {},
42673     cb: false,
42674     displayField : false,
42675     tipField : false,
42676     
42677     
42678     defaultAutoCreate : {
42679         tag: 'div',
42680         cls: 'x-cbarray-item',
42681         cn : [ 
42682             { tag: 'div' },
42683             {
42684                 tag: 'img',
42685                 width:16,
42686                 height : 16,
42687                 src : Roo.BLANK_IMAGE_URL ,
42688                 align: 'center'
42689             }
42690         ]
42691         
42692     },
42693     
42694  
42695     onRender : function(ct, position)
42696     {
42697         Roo.form.Field.superclass.onRender.call(this, ct, position);
42698         
42699         if(!this.el){
42700             var cfg = this.getAutoCreate();
42701             this.el = ct.createChild(cfg, position);
42702         }
42703         
42704         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42705         
42706         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42707             this.cb.renderer(this.data) :
42708             String.format('{0}',this.data[this.displayField]);
42709         
42710             
42711         this.el.child('div').dom.setAttribute('qtip',
42712                         String.format('{0}',this.data[this.tipField])
42713         );
42714         
42715         this.el.child('img').on('click', this.remove, this);
42716         
42717     },
42718    
42719     remove : function()
42720     {
42721         if(this.cb.disabled){
42722             return;
42723         }
42724         
42725         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42726             this.cb.items.remove(this);
42727             this.el.child('img').un('click', this.remove, this);
42728             this.el.remove();
42729             this.cb.updateHiddenEl();
42730
42731             this.cb.fireEvent('remove', this.cb, this);
42732         }
42733         
42734     }
42735 });/*
42736  * RooJS Library 1.1.1
42737  * Copyright(c) 2008-2011  Alan Knowles
42738  *
42739  * License - LGPL
42740  */
42741  
42742
42743 /**
42744  * @class Roo.form.ComboNested
42745  * @extends Roo.form.ComboBox
42746  * A combobox for that allows selection of nested items in a list,
42747  * eg.
42748  *
42749  *  Book
42750  *    -> red
42751  *    -> green
42752  *  Table
42753  *    -> square
42754  *      ->red
42755  *      ->green
42756  *    -> rectangle
42757  *      ->green
42758  *      
42759  * 
42760  * @constructor
42761  * Create a new ComboNested
42762  * @param {Object} config Configuration options
42763  */
42764 Roo.form.ComboNested = function(config){
42765     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42766     // should verify some data...
42767     // like
42768     // hiddenName = required..
42769     // displayField = required
42770     // valudField == required
42771     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42772     var _t = this;
42773     Roo.each(req, function(e) {
42774         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42775             throw "Roo.form.ComboNested : missing value for: " + e;
42776         }
42777     });
42778      
42779     
42780 };
42781
42782 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42783    
42784     /*
42785      * @config {Number} max Number of columns to show
42786      */
42787     
42788     maxColumns : 3,
42789    
42790     list : null, // the outermost div..
42791     innerLists : null, // the
42792     views : null,
42793     stores : null,
42794     // private
42795     loadingChildren : false,
42796     
42797     onRender : function(ct, position)
42798     {
42799         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42800         
42801         if(this.hiddenName){
42802             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42803                     'before', true);
42804             this.hiddenField.value =
42805                 this.hiddenValue !== undefined ? this.hiddenValue :
42806                 this.value !== undefined ? this.value : '';
42807
42808             // prevent input submission
42809             this.el.dom.removeAttribute('name');
42810              
42811              
42812         }
42813         
42814         if(Roo.isGecko){
42815             this.el.dom.setAttribute('autocomplete', 'off');
42816         }
42817
42818         var cls = 'x-combo-list';
42819
42820         this.list = new Roo.Layer({
42821             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42822         });
42823
42824         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42825         this.list.setWidth(lw);
42826         this.list.swallowEvent('mousewheel');
42827         this.assetHeight = 0;
42828
42829         if(this.title){
42830             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42831             this.assetHeight += this.header.getHeight();
42832         }
42833         this.innerLists = [];
42834         this.views = [];
42835         this.stores = [];
42836         for (var i =0 ; i < this.maxColumns; i++) {
42837             this.onRenderList( cls, i);
42838         }
42839         
42840         // always needs footer, as we are going to have an 'OK' button.
42841         this.footer = this.list.createChild({cls:cls+'-ft'});
42842         this.pageTb = new Roo.Toolbar(this.footer);  
42843         var _this = this;
42844         this.pageTb.add(  {
42845             
42846             text: 'Done',
42847             handler: function()
42848             {
42849                 _this.collapse();
42850             }
42851         });
42852         
42853         if ( this.allowBlank && !this.disableClear) {
42854             
42855             this.pageTb.add(new Roo.Toolbar.Fill(), {
42856                 cls: 'x-btn-icon x-btn-clear',
42857                 text: '&#160;',
42858                 handler: function()
42859                 {
42860                     _this.collapse();
42861                     _this.clearValue();
42862                     _this.onSelect(false, -1);
42863                 }
42864             });
42865         }
42866         if (this.footer) {
42867             this.assetHeight += this.footer.getHeight();
42868         }
42869         
42870     },
42871     onRenderList : function (  cls, i)
42872     {
42873         
42874         var lw = Math.floor(
42875                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42876         );
42877         
42878         this.list.setWidth(lw); // default to '1'
42879
42880         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42881         //il.on('mouseover', this.onViewOver, this, { list:  i });
42882         //il.on('mousemove', this.onViewMove, this, { list:  i });
42883         il.setWidth(lw);
42884         il.setStyle({ 'overflow-x' : 'hidden'});
42885
42886         if(!this.tpl){
42887             this.tpl = new Roo.Template({
42888                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42889                 isEmpty: function (value, allValues) {
42890                     //Roo.log(value);
42891                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42892                     return dl ? 'has-children' : 'no-children'
42893                 }
42894             });
42895         }
42896         
42897         var store  = this.store;
42898         if (i > 0) {
42899             store  = new Roo.data.SimpleStore({
42900                 //fields : this.store.reader.meta.fields,
42901                 reader : this.store.reader,
42902                 data : [ ]
42903             });
42904         }
42905         this.stores[i]  = store;
42906                   
42907         var view = this.views[i] = new Roo.View(
42908             il,
42909             this.tpl,
42910             {
42911                 singleSelect:true,
42912                 store: store,
42913                 selectedClass: this.selectedClass
42914             }
42915         );
42916         view.getEl().setWidth(lw);
42917         view.getEl().setStyle({
42918             position: i < 1 ? 'relative' : 'absolute',
42919             top: 0,
42920             left: (i * lw ) + 'px',
42921             display : i > 0 ? 'none' : 'block'
42922         });
42923         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42924         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42925         //view.on('click', this.onViewClick, this, { list : i });
42926
42927         store.on('beforeload', this.onBeforeLoad, this);
42928         store.on('load',  this.onLoad, this, { list  : i});
42929         store.on('loadexception', this.onLoadException, this);
42930
42931         // hide the other vies..
42932         
42933         
42934         
42935     },
42936       
42937     restrictHeight : function()
42938     {
42939         var mh = 0;
42940         Roo.each(this.innerLists, function(il,i) {
42941             var el = this.views[i].getEl();
42942             el.dom.style.height = '';
42943             var inner = el.dom;
42944             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42945             // only adjust heights on other ones..
42946             mh = Math.max(h, mh);
42947             if (i < 1) {
42948                 
42949                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42950                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42951                
42952             }
42953             
42954             
42955         }, this);
42956         
42957         this.list.beginUpdate();
42958         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42959         this.list.alignTo(this.el, this.listAlign);
42960         this.list.endUpdate();
42961         
42962     },
42963      
42964     
42965     // -- store handlers..
42966     // private
42967     onBeforeLoad : function()
42968     {
42969         if(!this.hasFocus){
42970             return;
42971         }
42972         this.innerLists[0].update(this.loadingText ?
42973                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42974         this.restrictHeight();
42975         this.selectedIndex = -1;
42976     },
42977     // private
42978     onLoad : function(a,b,c,d)
42979     {
42980         if (!this.loadingChildren) {
42981             // then we are loading the top level. - hide the children
42982             for (var i = 1;i < this.views.length; i++) {
42983                 this.views[i].getEl().setStyle({ display : 'none' });
42984             }
42985             var lw = Math.floor(
42986                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42987             );
42988         
42989              this.list.setWidth(lw); // default to '1'
42990
42991             
42992         }
42993         if(!this.hasFocus){
42994             return;
42995         }
42996         
42997         if(this.store.getCount() > 0) {
42998             this.expand();
42999             this.restrictHeight();   
43000         } else {
43001             this.onEmptyResults();
43002         }
43003         
43004         if (!this.loadingChildren) {
43005             this.selectActive();
43006         }
43007         /*
43008         this.stores[1].loadData([]);
43009         this.stores[2].loadData([]);
43010         this.views
43011         */    
43012     
43013         //this.el.focus();
43014     },
43015     
43016     
43017     // private
43018     onLoadException : function()
43019     {
43020         this.collapse();
43021         Roo.log(this.store.reader.jsonData);
43022         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43023             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43024         }
43025         
43026         
43027     },
43028     // no cleaning of leading spaces on blur here.
43029     cleanLeadingSpace : function(e) { },
43030     
43031
43032     onSelectChange : function (view, sels, opts )
43033     {
43034         var ix = view.getSelectedIndexes();
43035          
43036         if (opts.list > this.maxColumns - 2) {
43037             if (view.store.getCount()<  1) {
43038                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43039
43040             } else  {
43041                 if (ix.length) {
43042                     // used to clear ?? but if we are loading unselected 
43043                     this.setFromData(view.store.getAt(ix[0]).data);
43044                 }
43045                 
43046             }
43047             
43048             return;
43049         }
43050         
43051         if (!ix.length) {
43052             // this get's fired when trigger opens..
43053            // this.setFromData({});
43054             var str = this.stores[opts.list+1];
43055             str.data.clear(); // removeall wihtout the fire events..
43056             return;
43057         }
43058         
43059         var rec = view.store.getAt(ix[0]);
43060          
43061         this.setFromData(rec.data);
43062         this.fireEvent('select', this, rec, ix[0]);
43063         
43064         var lw = Math.floor(
43065              (
43066                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43067              ) / this.maxColumns
43068         );
43069         this.loadingChildren = true;
43070         this.stores[opts.list+1].loadDataFromChildren( rec );
43071         this.loadingChildren = false;
43072         var dl = this.stores[opts.list+1]. getTotalCount();
43073         
43074         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43075         
43076         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43077         for (var i = opts.list+2; i < this.views.length;i++) {
43078             this.views[i].getEl().setStyle({ display : 'none' });
43079         }
43080         
43081         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43082         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43083         
43084         if (this.isLoading) {
43085            // this.selectActive(opts.list);
43086         }
43087          
43088     },
43089     
43090     
43091     
43092     
43093     onDoubleClick : function()
43094     {
43095         this.collapse(); //??
43096     },
43097     
43098      
43099     
43100     
43101     
43102     // private
43103     recordToStack : function(store, prop, value, stack)
43104     {
43105         var cstore = new Roo.data.SimpleStore({
43106             //fields : this.store.reader.meta.fields, // we need array reader.. for
43107             reader : this.store.reader,
43108             data : [ ]
43109         });
43110         var _this = this;
43111         var record  = false;
43112         var srec = false;
43113         if(store.getCount() < 1){
43114             return false;
43115         }
43116         store.each(function(r){
43117             if(r.data[prop] == value){
43118                 record = r;
43119             srec = r;
43120                 return false;
43121             }
43122             if (r.data.cn && r.data.cn.length) {
43123                 cstore.loadDataFromChildren( r);
43124                 var cret = _this.recordToStack(cstore, prop, value, stack);
43125                 if (cret !== false) {
43126                     record = cret;
43127                     srec = r;
43128                     return false;
43129                 }
43130             }
43131              
43132             return true;
43133         });
43134         if (record == false) {
43135             return false
43136         }
43137         stack.unshift(srec);
43138         return record;
43139     },
43140     
43141     /*
43142      * find the stack of stores that match our value.
43143      *
43144      * 
43145      */
43146     
43147     selectActive : function ()
43148     {
43149         // if store is not loaded, then we will need to wait for that to happen first.
43150         var stack = [];
43151         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43152         for (var i = 0; i < stack.length; i++ ) {
43153             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43154         }
43155         
43156     }
43157         
43158          
43159     
43160     
43161     
43162     
43163 });/*
43164  * Based on:
43165  * Ext JS Library 1.1.1
43166  * Copyright(c) 2006-2007, Ext JS, LLC.
43167  *
43168  * Originally Released Under LGPL - original licence link has changed is not relivant.
43169  *
43170  * Fork - LGPL
43171  * <script type="text/javascript">
43172  */
43173 /**
43174  * @class Roo.form.Checkbox
43175  * @extends Roo.form.Field
43176  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43177  * @constructor
43178  * Creates a new Checkbox
43179  * @param {Object} config Configuration options
43180  */
43181 Roo.form.Checkbox = function(config){
43182     Roo.form.Checkbox.superclass.constructor.call(this, config);
43183     this.addEvents({
43184         /**
43185          * @event check
43186          * Fires when the checkbox is checked or unchecked.
43187              * @param {Roo.form.Checkbox} this This checkbox
43188              * @param {Boolean} checked The new checked value
43189              */
43190         check : true
43191     });
43192 };
43193
43194 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43195     /**
43196      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43197      */
43198     focusClass : undefined,
43199     /**
43200      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43201      */
43202     fieldClass: "x-form-field",
43203     /**
43204      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43205      */
43206     checked: false,
43207     /**
43208      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43209      * {tag: "input", type: "checkbox", autocomplete: "off"})
43210      */
43211     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43212     /**
43213      * @cfg {String} boxLabel The text that appears beside the checkbox
43214      */
43215     boxLabel : "",
43216     /**
43217      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43218      */  
43219     inputValue : '1',
43220     /**
43221      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43222      */
43223      valueOff: '0', // value when not checked..
43224
43225     actionMode : 'viewEl', 
43226     //
43227     // private
43228     itemCls : 'x-menu-check-item x-form-item',
43229     groupClass : 'x-menu-group-item',
43230     inputType : 'hidden',
43231     
43232     
43233     inSetChecked: false, // check that we are not calling self...
43234     
43235     inputElement: false, // real input element?
43236     basedOn: false, // ????
43237     
43238     isFormField: true, // not sure where this is needed!!!!
43239
43240     onResize : function(){
43241         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43242         if(!this.boxLabel){
43243             this.el.alignTo(this.wrap, 'c-c');
43244         }
43245     },
43246
43247     initEvents : function(){
43248         Roo.form.Checkbox.superclass.initEvents.call(this);
43249         this.el.on("click", this.onClick,  this);
43250         this.el.on("change", this.onClick,  this);
43251     },
43252
43253
43254     getResizeEl : function(){
43255         return this.wrap;
43256     },
43257
43258     getPositionEl : function(){
43259         return this.wrap;
43260     },
43261
43262     // private
43263     onRender : function(ct, position){
43264         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43265         /*
43266         if(this.inputValue !== undefined){
43267             this.el.dom.value = this.inputValue;
43268         }
43269         */
43270         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43271         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43272         var viewEl = this.wrap.createChild({ 
43273             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43274         this.viewEl = viewEl;   
43275         this.wrap.on('click', this.onClick,  this); 
43276         
43277         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43278         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43279         
43280         
43281         
43282         if(this.boxLabel){
43283             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43284         //    viewEl.on('click', this.onClick,  this); 
43285         }
43286         //if(this.checked){
43287             this.setChecked(this.checked);
43288         //}else{
43289             //this.checked = this.el.dom;
43290         //}
43291
43292     },
43293
43294     // private
43295     initValue : Roo.emptyFn,
43296
43297     /**
43298      * Returns the checked state of the checkbox.
43299      * @return {Boolean} True if checked, else false
43300      */
43301     getValue : function(){
43302         if(this.el){
43303             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43304         }
43305         return this.valueOff;
43306         
43307     },
43308
43309         // private
43310     onClick : function(){ 
43311         if (this.disabled) {
43312             return;
43313         }
43314         this.setChecked(!this.checked);
43315
43316         //if(this.el.dom.checked != this.checked){
43317         //    this.setValue(this.el.dom.checked);
43318        // }
43319     },
43320
43321     /**
43322      * Sets the checked state of the checkbox.
43323      * On is always based on a string comparison between inputValue and the param.
43324      * @param {Boolean/String} value - the value to set 
43325      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43326      */
43327     setValue : function(v,suppressEvent){
43328         
43329         
43330         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43331         //if(this.el && this.el.dom){
43332         //    this.el.dom.checked = this.checked;
43333         //    this.el.dom.defaultChecked = this.checked;
43334         //}
43335         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43336         //this.fireEvent("check", this, this.checked);
43337     },
43338     // private..
43339     setChecked : function(state,suppressEvent)
43340     {
43341         if (this.inSetChecked) {
43342             this.checked = state;
43343             return;
43344         }
43345         
43346     
43347         if(this.wrap){
43348             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43349         }
43350         this.checked = state;
43351         if(suppressEvent !== true){
43352             this.fireEvent('check', this, state);
43353         }
43354         this.inSetChecked = true;
43355         this.el.dom.value = state ? this.inputValue : this.valueOff;
43356         this.inSetChecked = false;
43357         
43358     },
43359     // handle setting of hidden value by some other method!!?!?
43360     setFromHidden: function()
43361     {
43362         if(!this.el){
43363             return;
43364         }
43365         //console.log("SET FROM HIDDEN");
43366         //alert('setFrom hidden');
43367         this.setValue(this.el.dom.value);
43368     },
43369     
43370     onDestroy : function()
43371     {
43372         if(this.viewEl){
43373             Roo.get(this.viewEl).remove();
43374         }
43375          
43376         Roo.form.Checkbox.superclass.onDestroy.call(this);
43377     },
43378     
43379     setBoxLabel : function(str)
43380     {
43381         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43382     }
43383
43384 });/*
43385  * Based on:
43386  * Ext JS Library 1.1.1
43387  * Copyright(c) 2006-2007, Ext JS, LLC.
43388  *
43389  * Originally Released Under LGPL - original licence link has changed is not relivant.
43390  *
43391  * Fork - LGPL
43392  * <script type="text/javascript">
43393  */
43394  
43395 /**
43396  * @class Roo.form.Radio
43397  * @extends Roo.form.Checkbox
43398  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43399  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43400  * @constructor
43401  * Creates a new Radio
43402  * @param {Object} config Configuration options
43403  */
43404 Roo.form.Radio = function(){
43405     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43406 };
43407 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43408     inputType: 'radio',
43409
43410     /**
43411      * If this radio is part of a group, it will return the selected value
43412      * @return {String}
43413      */
43414     getGroupValue : function(){
43415         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43416     },
43417     
43418     
43419     onRender : function(ct, position){
43420         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43421         
43422         if(this.inputValue !== undefined){
43423             this.el.dom.value = this.inputValue;
43424         }
43425          
43426         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43427         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43428         //var viewEl = this.wrap.createChild({ 
43429         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43430         //this.viewEl = viewEl;   
43431         //this.wrap.on('click', this.onClick,  this); 
43432         
43433         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43434         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43435         
43436         
43437         
43438         if(this.boxLabel){
43439             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43440         //    viewEl.on('click', this.onClick,  this); 
43441         }
43442          if(this.checked){
43443             this.el.dom.checked =   'checked' ;
43444         }
43445          
43446     } 
43447     
43448     
43449 });//<script type="text/javascript">
43450
43451 /*
43452  * Based  Ext JS Library 1.1.1
43453  * Copyright(c) 2006-2007, Ext JS, LLC.
43454  * LGPL
43455  *
43456  */
43457  
43458 /**
43459  * @class Roo.HtmlEditorCore
43460  * @extends Roo.Component
43461  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43462  *
43463  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43464  */
43465
43466 Roo.HtmlEditorCore = function(config){
43467     
43468     
43469     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43470     
43471     
43472     this.addEvents({
43473         /**
43474          * @event initialize
43475          * Fires when the editor is fully initialized (including the iframe)
43476          * @param {Roo.HtmlEditorCore} this
43477          */
43478         initialize: true,
43479         /**
43480          * @event activate
43481          * Fires when the editor is first receives the focus. Any insertion must wait
43482          * until after this event.
43483          * @param {Roo.HtmlEditorCore} this
43484          */
43485         activate: true,
43486          /**
43487          * @event beforesync
43488          * Fires before the textarea is updated with content from the editor iframe. Return false
43489          * to cancel the sync.
43490          * @param {Roo.HtmlEditorCore} this
43491          * @param {String} html
43492          */
43493         beforesync: true,
43494          /**
43495          * @event beforepush
43496          * Fires before the iframe editor is updated with content from the textarea. Return false
43497          * to cancel the push.
43498          * @param {Roo.HtmlEditorCore} this
43499          * @param {String} html
43500          */
43501         beforepush: true,
43502          /**
43503          * @event sync
43504          * Fires when the textarea is updated with content from the editor iframe.
43505          * @param {Roo.HtmlEditorCore} this
43506          * @param {String} html
43507          */
43508         sync: true,
43509          /**
43510          * @event push
43511          * Fires when the iframe editor is updated with content from the textarea.
43512          * @param {Roo.HtmlEditorCore} this
43513          * @param {String} html
43514          */
43515         push: true,
43516         
43517         /**
43518          * @event editorevent
43519          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43520          * @param {Roo.HtmlEditorCore} this
43521          */
43522         editorevent: true
43523         
43524     });
43525     
43526     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43527     
43528     // defaults : white / black...
43529     this.applyBlacklists();
43530     
43531     
43532     
43533 };
43534
43535
43536 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43537
43538
43539      /**
43540      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43541      */
43542     
43543     owner : false,
43544     
43545      /**
43546      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43547      *                        Roo.resizable.
43548      */
43549     resizable : false,
43550      /**
43551      * @cfg {Number} height (in pixels)
43552      */   
43553     height: 300,
43554    /**
43555      * @cfg {Number} width (in pixels)
43556      */   
43557     width: 500,
43558     
43559     /**
43560      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43561      * 
43562      */
43563     stylesheets: false,
43564     
43565     // id of frame..
43566     frameId: false,
43567     
43568     // private properties
43569     validationEvent : false,
43570     deferHeight: true,
43571     initialized : false,
43572     activated : false,
43573     sourceEditMode : false,
43574     onFocus : Roo.emptyFn,
43575     iframePad:3,
43576     hideMode:'offsets',
43577     
43578     clearUp: true,
43579     
43580     // blacklist + whitelisted elements..
43581     black: false,
43582     white: false,
43583      
43584     bodyCls : '',
43585
43586     /**
43587      * Protected method that will not generally be called directly. It
43588      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43589      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43590      */
43591     getDocMarkup : function(){
43592         // body styles..
43593         var st = '';
43594         
43595         // inherit styels from page...?? 
43596         if (this.stylesheets === false) {
43597             
43598             Roo.get(document.head).select('style').each(function(node) {
43599                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43600             });
43601             
43602             Roo.get(document.head).select('link').each(function(node) { 
43603                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43604             });
43605             
43606         } else if (!this.stylesheets.length) {
43607                 // simple..
43608                 st = '<style type="text/css">' +
43609                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43610                    '</style>';
43611         } else { 
43612             st = '<style type="text/css">' +
43613                     this.stylesheets +
43614                 '</style>';
43615         }
43616         
43617         st +=  '<style type="text/css">' +
43618             'IMG { cursor: pointer } ' +
43619         '</style>';
43620
43621         var cls = 'roo-htmleditor-body';
43622         
43623         if(this.bodyCls.length){
43624             cls += ' ' + this.bodyCls;
43625         }
43626         
43627         return '<html><head>' + st  +
43628             //<style type="text/css">' +
43629             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43630             //'</style>' +
43631             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43632     },
43633
43634     // private
43635     onRender : function(ct, position)
43636     {
43637         var _t = this;
43638         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43639         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43640         
43641         
43642         this.el.dom.style.border = '0 none';
43643         this.el.dom.setAttribute('tabIndex', -1);
43644         this.el.addClass('x-hidden hide');
43645         
43646         
43647         
43648         if(Roo.isIE){ // fix IE 1px bogus margin
43649             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43650         }
43651        
43652         
43653         this.frameId = Roo.id();
43654         
43655          
43656         
43657         var iframe = this.owner.wrap.createChild({
43658             tag: 'iframe',
43659             cls: 'form-control', // bootstrap..
43660             id: this.frameId,
43661             name: this.frameId,
43662             frameBorder : 'no',
43663             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43664         }, this.el
43665         );
43666         
43667         
43668         this.iframe = iframe.dom;
43669
43670          this.assignDocWin();
43671         
43672         this.doc.designMode = 'on';
43673        
43674         this.doc.open();
43675         this.doc.write(this.getDocMarkup());
43676         this.doc.close();
43677
43678         
43679         var task = { // must defer to wait for browser to be ready
43680             run : function(){
43681                 //console.log("run task?" + this.doc.readyState);
43682                 this.assignDocWin();
43683                 if(this.doc.body || this.doc.readyState == 'complete'){
43684                     try {
43685                         this.doc.designMode="on";
43686                     } catch (e) {
43687                         return;
43688                     }
43689                     Roo.TaskMgr.stop(task);
43690                     this.initEditor.defer(10, this);
43691                 }
43692             },
43693             interval : 10,
43694             duration: 10000,
43695             scope: this
43696         };
43697         Roo.TaskMgr.start(task);
43698
43699     },
43700
43701     // private
43702     onResize : function(w, h)
43703     {
43704          Roo.log('resize: ' +w + ',' + h );
43705         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43706         if(!this.iframe){
43707             return;
43708         }
43709         if(typeof w == 'number'){
43710             
43711             this.iframe.style.width = w + 'px';
43712         }
43713         if(typeof h == 'number'){
43714             
43715             this.iframe.style.height = h + 'px';
43716             if(this.doc){
43717                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43718             }
43719         }
43720         
43721     },
43722
43723     /**
43724      * Toggles the editor between standard and source edit mode.
43725      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43726      */
43727     toggleSourceEdit : function(sourceEditMode){
43728         
43729         this.sourceEditMode = sourceEditMode === true;
43730         
43731         if(this.sourceEditMode){
43732  
43733             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43734             
43735         }else{
43736             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43737             //this.iframe.className = '';
43738             this.deferFocus();
43739         }
43740         //this.setSize(this.owner.wrap.getSize());
43741         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43742     },
43743
43744     
43745   
43746
43747     /**
43748      * Protected method that will not generally be called directly. If you need/want
43749      * custom HTML cleanup, this is the method you should override.
43750      * @param {String} html The HTML to be cleaned
43751      * return {String} The cleaned HTML
43752      */
43753     cleanHtml : function(html){
43754         html = String(html);
43755         if(html.length > 5){
43756             if(Roo.isSafari){ // strip safari nonsense
43757                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43758             }
43759         }
43760         if(html == '&nbsp;'){
43761             html = '';
43762         }
43763         return html;
43764     },
43765
43766     /**
43767      * HTML Editor -> Textarea
43768      * Protected method that will not generally be called directly. Syncs the contents
43769      * of the editor iframe with the textarea.
43770      */
43771     syncValue : function(){
43772         if(this.initialized){
43773             var bd = (this.doc.body || this.doc.documentElement);
43774             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43775             var html = bd.innerHTML;
43776             if(Roo.isSafari){
43777                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43778                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43779                 if(m && m[1]){
43780                     html = '<div style="'+m[0]+'">' + html + '</div>';
43781                 }
43782             }
43783             html = this.cleanHtml(html);
43784             // fix up the special chars.. normaly like back quotes in word...
43785             // however we do not want to do this with chinese..
43786             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43787                 
43788                 var cc = match.charCodeAt();
43789
43790                 // Get the character value, handling surrogate pairs
43791                 if (match.length == 2) {
43792                     // It's a surrogate pair, calculate the Unicode code point
43793                     var high = match.charCodeAt(0) - 0xD800;
43794                     var low  = match.charCodeAt(1) - 0xDC00;
43795                     cc = (high * 0x400) + low + 0x10000;
43796                 }  else if (
43797                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43798                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43799                     (cc >= 0xf900 && cc < 0xfb00 )
43800                 ) {
43801                         return match;
43802                 }  
43803          
43804                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43805                 return "&#" + cc + ";";
43806                 
43807                 
43808             });
43809             
43810             
43811              
43812             if(this.owner.fireEvent('beforesync', this, html) !== false){
43813                 this.el.dom.value = html;
43814                 this.owner.fireEvent('sync', this, html);
43815             }
43816         }
43817     },
43818
43819     /**
43820      * Protected method that will not generally be called directly. Pushes the value of the textarea
43821      * into the iframe editor.
43822      */
43823     pushValue : function(){
43824         if(this.initialized){
43825             var v = this.el.dom.value.trim();
43826             
43827 //            if(v.length < 1){
43828 //                v = '&#160;';
43829 //            }
43830             
43831             if(this.owner.fireEvent('beforepush', this, v) !== false){
43832                 var d = (this.doc.body || this.doc.documentElement);
43833                 d.innerHTML = v;
43834                 this.cleanUpPaste();
43835                 this.el.dom.value = d.innerHTML;
43836                 this.owner.fireEvent('push', this, v);
43837             }
43838         }
43839     },
43840
43841     // private
43842     deferFocus : function(){
43843         this.focus.defer(10, this);
43844     },
43845
43846     // doc'ed in Field
43847     focus : function(){
43848         if(this.win && !this.sourceEditMode){
43849             this.win.focus();
43850         }else{
43851             this.el.focus();
43852         }
43853     },
43854     
43855     assignDocWin: function()
43856     {
43857         var iframe = this.iframe;
43858         
43859          if(Roo.isIE){
43860             this.doc = iframe.contentWindow.document;
43861             this.win = iframe.contentWindow;
43862         } else {
43863 //            if (!Roo.get(this.frameId)) {
43864 //                return;
43865 //            }
43866 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43867 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43868             
43869             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43870                 return;
43871             }
43872             
43873             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43874             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43875         }
43876     },
43877     
43878     // private
43879     initEditor : function(){
43880         //console.log("INIT EDITOR");
43881         this.assignDocWin();
43882         
43883         
43884         
43885         this.doc.designMode="on";
43886         this.doc.open();
43887         this.doc.write(this.getDocMarkup());
43888         this.doc.close();
43889         
43890         var dbody = (this.doc.body || this.doc.documentElement);
43891         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43892         // this copies styles from the containing element into thsi one..
43893         // not sure why we need all of this..
43894         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43895         
43896         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43897         //ss['background-attachment'] = 'fixed'; // w3c
43898         dbody.bgProperties = 'fixed'; // ie
43899         //Roo.DomHelper.applyStyles(dbody, ss);
43900         Roo.EventManager.on(this.doc, {
43901             //'mousedown': this.onEditorEvent,
43902             'mouseup': this.onEditorEvent,
43903             'dblclick': this.onEditorEvent,
43904             'click': this.onEditorEvent,
43905             'keyup': this.onEditorEvent,
43906             buffer:100,
43907             scope: this
43908         });
43909         if(Roo.isGecko){
43910             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43911         }
43912         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43913             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43914         }
43915         this.initialized = true;
43916
43917         this.owner.fireEvent('initialize', this);
43918         this.pushValue();
43919     },
43920
43921     // private
43922     onDestroy : function(){
43923         
43924         
43925         
43926         if(this.rendered){
43927             
43928             //for (var i =0; i < this.toolbars.length;i++) {
43929             //    // fixme - ask toolbars for heights?
43930             //    this.toolbars[i].onDestroy();
43931            // }
43932             
43933             //this.wrap.dom.innerHTML = '';
43934             //this.wrap.remove();
43935         }
43936     },
43937
43938     // private
43939     onFirstFocus : function(){
43940         
43941         this.assignDocWin();
43942         
43943         
43944         this.activated = true;
43945          
43946     
43947         if(Roo.isGecko){ // prevent silly gecko errors
43948             this.win.focus();
43949             var s = this.win.getSelection();
43950             if(!s.focusNode || s.focusNode.nodeType != 3){
43951                 var r = s.getRangeAt(0);
43952                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43953                 r.collapse(true);
43954                 this.deferFocus();
43955             }
43956             try{
43957                 this.execCmd('useCSS', true);
43958                 this.execCmd('styleWithCSS', false);
43959             }catch(e){}
43960         }
43961         this.owner.fireEvent('activate', this);
43962     },
43963
43964     // private
43965     adjustFont: function(btn){
43966         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43967         //if(Roo.isSafari){ // safari
43968         //    adjust *= 2;
43969        // }
43970         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43971         if(Roo.isSafari){ // safari
43972             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43973             v =  (v < 10) ? 10 : v;
43974             v =  (v > 48) ? 48 : v;
43975             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43976             
43977         }
43978         
43979         
43980         v = Math.max(1, v+adjust);
43981         
43982         this.execCmd('FontSize', v  );
43983     },
43984
43985     onEditorEvent : function(e)
43986     {
43987         this.owner.fireEvent('editorevent', this, e);
43988       //  this.updateToolbar();
43989         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43990     },
43991
43992     insertTag : function(tg)
43993     {
43994         // could be a bit smarter... -> wrap the current selected tRoo..
43995         if (tg.toLowerCase() == 'span' ||
43996             tg.toLowerCase() == 'code' ||
43997             tg.toLowerCase() == 'sup' ||
43998             tg.toLowerCase() == 'sub' 
43999             ) {
44000             
44001             range = this.createRange(this.getSelection());
44002             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44003             wrappingNode.appendChild(range.extractContents());
44004             range.insertNode(wrappingNode);
44005
44006             return;
44007             
44008             
44009             
44010         }
44011         this.execCmd("formatblock",   tg);
44012         
44013     },
44014     
44015     insertText : function(txt)
44016     {
44017         
44018         
44019         var range = this.createRange();
44020         range.deleteContents();
44021                //alert(Sender.getAttribute('label'));
44022                
44023         range.insertNode(this.doc.createTextNode(txt));
44024     } ,
44025     
44026      
44027
44028     /**
44029      * Executes a Midas editor command on the editor document and performs necessary focus and
44030      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44031      * @param {String} cmd The Midas command
44032      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44033      */
44034     relayCmd : function(cmd, value){
44035         this.win.focus();
44036         this.execCmd(cmd, value);
44037         this.owner.fireEvent('editorevent', this);
44038         //this.updateToolbar();
44039         this.owner.deferFocus();
44040     },
44041
44042     /**
44043      * Executes a Midas editor command directly on the editor document.
44044      * For visual commands, you should use {@link #relayCmd} instead.
44045      * <b>This should only be called after the editor is initialized.</b>
44046      * @param {String} cmd The Midas command
44047      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44048      */
44049     execCmd : function(cmd, value){
44050         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44051         this.syncValue();
44052     },
44053  
44054  
44055    
44056     /**
44057      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44058      * to insert tRoo.
44059      * @param {String} text | dom node.. 
44060      */
44061     insertAtCursor : function(text)
44062     {
44063         
44064         if(!this.activated){
44065             return;
44066         }
44067         /*
44068         if(Roo.isIE){
44069             this.win.focus();
44070             var r = this.doc.selection.createRange();
44071             if(r){
44072                 r.collapse(true);
44073                 r.pasteHTML(text);
44074                 this.syncValue();
44075                 this.deferFocus();
44076             
44077             }
44078             return;
44079         }
44080         */
44081         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44082             this.win.focus();
44083             
44084             
44085             // from jquery ui (MIT licenced)
44086             var range, node;
44087             var win = this.win;
44088             
44089             if (win.getSelection && win.getSelection().getRangeAt) {
44090                 range = win.getSelection().getRangeAt(0);
44091                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44092                 range.insertNode(node);
44093             } else if (win.document.selection && win.document.selection.createRange) {
44094                 // no firefox support
44095                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44096                 win.document.selection.createRange().pasteHTML(txt);
44097             } else {
44098                 // no firefox support
44099                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44100                 this.execCmd('InsertHTML', txt);
44101             } 
44102             
44103             this.syncValue();
44104             
44105             this.deferFocus();
44106         }
44107     },
44108  // private
44109     mozKeyPress : function(e){
44110         if(e.ctrlKey){
44111             var c = e.getCharCode(), cmd;
44112           
44113             if(c > 0){
44114                 c = String.fromCharCode(c).toLowerCase();
44115                 switch(c){
44116                     case 'b':
44117                         cmd = 'bold';
44118                         break;
44119                     case 'i':
44120                         cmd = 'italic';
44121                         break;
44122                     
44123                     case 'u':
44124                         cmd = 'underline';
44125                         break;
44126                     
44127                     case 'v':
44128                         this.cleanUpPaste.defer(100, this);
44129                         return;
44130                         
44131                 }
44132                 if(cmd){
44133                     this.win.focus();
44134                     this.execCmd(cmd);
44135                     this.deferFocus();
44136                     e.preventDefault();
44137                 }
44138                 
44139             }
44140         }
44141     },
44142
44143     // private
44144     fixKeys : function(){ // load time branching for fastest keydown performance
44145         if(Roo.isIE){
44146             return function(e){
44147                 var k = e.getKey(), r;
44148                 if(k == e.TAB){
44149                     e.stopEvent();
44150                     r = this.doc.selection.createRange();
44151                     if(r){
44152                         r.collapse(true);
44153                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44154                         this.deferFocus();
44155                     }
44156                     return;
44157                 }
44158                 
44159                 if(k == e.ENTER){
44160                     r = this.doc.selection.createRange();
44161                     if(r){
44162                         var target = r.parentElement();
44163                         if(!target || target.tagName.toLowerCase() != 'li'){
44164                             e.stopEvent();
44165                             r.pasteHTML('<br />');
44166                             r.collapse(false);
44167                             r.select();
44168                         }
44169                     }
44170                 }
44171                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44172                     this.cleanUpPaste.defer(100, this);
44173                     return;
44174                 }
44175                 
44176                 
44177             };
44178         }else if(Roo.isOpera){
44179             return function(e){
44180                 var k = e.getKey();
44181                 if(k == e.TAB){
44182                     e.stopEvent();
44183                     this.win.focus();
44184                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44185                     this.deferFocus();
44186                 }
44187                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44188                     this.cleanUpPaste.defer(100, this);
44189                     return;
44190                 }
44191                 
44192             };
44193         }else if(Roo.isSafari){
44194             return function(e){
44195                 var k = e.getKey();
44196                 
44197                 if(k == e.TAB){
44198                     e.stopEvent();
44199                     this.execCmd('InsertText','\t');
44200                     this.deferFocus();
44201                     return;
44202                 }
44203                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44204                     this.cleanUpPaste.defer(100, this);
44205                     return;
44206                 }
44207                 
44208              };
44209         }
44210     }(),
44211     
44212     getAllAncestors: function()
44213     {
44214         var p = this.getSelectedNode();
44215         var a = [];
44216         if (!p) {
44217             a.push(p); // push blank onto stack..
44218             p = this.getParentElement();
44219         }
44220         
44221         
44222         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44223             a.push(p);
44224             p = p.parentNode;
44225         }
44226         a.push(this.doc.body);
44227         return a;
44228     },
44229     lastSel : false,
44230     lastSelNode : false,
44231     
44232     
44233     getSelection : function() 
44234     {
44235         this.assignDocWin();
44236         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44237     },
44238     
44239     getSelectedNode: function() 
44240     {
44241         // this may only work on Gecko!!!
44242         
44243         // should we cache this!!!!
44244         
44245         
44246         
44247          
44248         var range = this.createRange(this.getSelection()).cloneRange();
44249         
44250         if (Roo.isIE) {
44251             var parent = range.parentElement();
44252             while (true) {
44253                 var testRange = range.duplicate();
44254                 testRange.moveToElementText(parent);
44255                 if (testRange.inRange(range)) {
44256                     break;
44257                 }
44258                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44259                     break;
44260                 }
44261                 parent = parent.parentElement;
44262             }
44263             return parent;
44264         }
44265         
44266         // is ancestor a text element.
44267         var ac =  range.commonAncestorContainer;
44268         if (ac.nodeType == 3) {
44269             ac = ac.parentNode;
44270         }
44271         
44272         var ar = ac.childNodes;
44273          
44274         var nodes = [];
44275         var other_nodes = [];
44276         var has_other_nodes = false;
44277         for (var i=0;i<ar.length;i++) {
44278             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44279                 continue;
44280             }
44281             // fullly contained node.
44282             
44283             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44284                 nodes.push(ar[i]);
44285                 continue;
44286             }
44287             
44288             // probably selected..
44289             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44290                 other_nodes.push(ar[i]);
44291                 continue;
44292             }
44293             // outer..
44294             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44295                 continue;
44296             }
44297             
44298             
44299             has_other_nodes = true;
44300         }
44301         if (!nodes.length && other_nodes.length) {
44302             nodes= other_nodes;
44303         }
44304         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44305             return false;
44306         }
44307         
44308         return nodes[0];
44309     },
44310     createRange: function(sel)
44311     {
44312         // this has strange effects when using with 
44313         // top toolbar - not sure if it's a great idea.
44314         //this.editor.contentWindow.focus();
44315         if (typeof sel != "undefined") {
44316             try {
44317                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44318             } catch(e) {
44319                 return this.doc.createRange();
44320             }
44321         } else {
44322             return this.doc.createRange();
44323         }
44324     },
44325     getParentElement: function()
44326     {
44327         
44328         this.assignDocWin();
44329         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44330         
44331         var range = this.createRange(sel);
44332          
44333         try {
44334             var p = range.commonAncestorContainer;
44335             while (p.nodeType == 3) { // text node
44336                 p = p.parentNode;
44337             }
44338             return p;
44339         } catch (e) {
44340             return null;
44341         }
44342     
44343     },
44344     /***
44345      *
44346      * Range intersection.. the hard stuff...
44347      *  '-1' = before
44348      *  '0' = hits..
44349      *  '1' = after.
44350      *         [ -- selected range --- ]
44351      *   [fail]                        [fail]
44352      *
44353      *    basically..
44354      *      if end is before start or  hits it. fail.
44355      *      if start is after end or hits it fail.
44356      *
44357      *   if either hits (but other is outside. - then it's not 
44358      *   
44359      *    
44360      **/
44361     
44362     
44363     // @see http://www.thismuchiknow.co.uk/?p=64.
44364     rangeIntersectsNode : function(range, node)
44365     {
44366         var nodeRange = node.ownerDocument.createRange();
44367         try {
44368             nodeRange.selectNode(node);
44369         } catch (e) {
44370             nodeRange.selectNodeContents(node);
44371         }
44372     
44373         var rangeStartRange = range.cloneRange();
44374         rangeStartRange.collapse(true);
44375     
44376         var rangeEndRange = range.cloneRange();
44377         rangeEndRange.collapse(false);
44378     
44379         var nodeStartRange = nodeRange.cloneRange();
44380         nodeStartRange.collapse(true);
44381     
44382         var nodeEndRange = nodeRange.cloneRange();
44383         nodeEndRange.collapse(false);
44384     
44385         return rangeStartRange.compareBoundaryPoints(
44386                  Range.START_TO_START, nodeEndRange) == -1 &&
44387                rangeEndRange.compareBoundaryPoints(
44388                  Range.START_TO_START, nodeStartRange) == 1;
44389         
44390          
44391     },
44392     rangeCompareNode : function(range, node)
44393     {
44394         var nodeRange = node.ownerDocument.createRange();
44395         try {
44396             nodeRange.selectNode(node);
44397         } catch (e) {
44398             nodeRange.selectNodeContents(node);
44399         }
44400         
44401         
44402         range.collapse(true);
44403     
44404         nodeRange.collapse(true);
44405      
44406         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44407         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44408          
44409         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44410         
44411         var nodeIsBefore   =  ss == 1;
44412         var nodeIsAfter    = ee == -1;
44413         
44414         if (nodeIsBefore && nodeIsAfter) {
44415             return 0; // outer
44416         }
44417         if (!nodeIsBefore && nodeIsAfter) {
44418             return 1; //right trailed.
44419         }
44420         
44421         if (nodeIsBefore && !nodeIsAfter) {
44422             return 2;  // left trailed.
44423         }
44424         // fully contined.
44425         return 3;
44426     },
44427
44428     // private? - in a new class?
44429     cleanUpPaste :  function()
44430     {
44431         // cleans up the whole document..
44432         Roo.log('cleanuppaste');
44433         
44434         this.cleanUpChildren(this.doc.body);
44435         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44436         if (clean != this.doc.body.innerHTML) {
44437             this.doc.body.innerHTML = clean;
44438         }
44439         
44440     },
44441     
44442     cleanWordChars : function(input) {// change the chars to hex code
44443         var he = Roo.HtmlEditorCore;
44444         
44445         var output = input;
44446         Roo.each(he.swapCodes, function(sw) { 
44447             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44448             
44449             output = output.replace(swapper, sw[1]);
44450         });
44451         
44452         return output;
44453     },
44454     
44455     
44456     cleanUpChildren : function (n)
44457     {
44458         if (!n.childNodes.length) {
44459             return;
44460         }
44461         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44462            this.cleanUpChild(n.childNodes[i]);
44463         }
44464     },
44465     
44466     
44467         
44468     
44469     cleanUpChild : function (node)
44470     {
44471         var ed = this;
44472         //console.log(node);
44473         if (node.nodeName == "#text") {
44474             // clean up silly Windows -- stuff?
44475             return; 
44476         }
44477         if (node.nodeName == "#comment") {
44478             node.parentNode.removeChild(node);
44479             // clean up silly Windows -- stuff?
44480             return; 
44481         }
44482         var lcname = node.tagName.toLowerCase();
44483         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44484         // whitelist of tags..
44485         
44486         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44487             // remove node.
44488             node.parentNode.removeChild(node);
44489             return;
44490             
44491         }
44492         
44493         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44494         
44495         // spans with no attributes - just remove them..
44496         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44497             remove_keep_children = true;
44498         }
44499         
44500         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44501         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44502         
44503         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44504         //    remove_keep_children = true;
44505         //}
44506         
44507         if (remove_keep_children) {
44508             this.cleanUpChildren(node);
44509             // inserts everything just before this node...
44510             while (node.childNodes.length) {
44511                 var cn = node.childNodes[0];
44512                 node.removeChild(cn);
44513                 node.parentNode.insertBefore(cn, node);
44514             }
44515             node.parentNode.removeChild(node);
44516             return;
44517         }
44518         
44519         if (!node.attributes || !node.attributes.length) {
44520             
44521           
44522             
44523             
44524             this.cleanUpChildren(node);
44525             return;
44526         }
44527         
44528         function cleanAttr(n,v)
44529         {
44530             
44531             if (v.match(/^\./) || v.match(/^\//)) {
44532                 return;
44533             }
44534             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44535                 return;
44536             }
44537             if (v.match(/^#/)) {
44538                 return;
44539             }
44540             if (v.match(/^\{/)) { // allow template editing.
44541                 return;
44542             }
44543 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44544             node.removeAttribute(n);
44545             
44546         }
44547         
44548         var cwhite = this.cwhite;
44549         var cblack = this.cblack;
44550             
44551         function cleanStyle(n,v)
44552         {
44553             if (v.match(/expression/)) { //XSS?? should we even bother..
44554                 node.removeAttribute(n);
44555                 return;
44556             }
44557             
44558             var parts = v.split(/;/);
44559             var clean = [];
44560             
44561             Roo.each(parts, function(p) {
44562                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44563                 if (!p.length) {
44564                     return true;
44565                 }
44566                 var l = p.split(':').shift().replace(/\s+/g,'');
44567                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44568                 
44569                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44570 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44571                     //node.removeAttribute(n);
44572                     return true;
44573                 }
44574                 //Roo.log()
44575                 // only allow 'c whitelisted system attributes'
44576                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44577 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44578                     //node.removeAttribute(n);
44579                     return true;
44580                 }
44581                 
44582                 
44583                  
44584                 
44585                 clean.push(p);
44586                 return true;
44587             });
44588             if (clean.length) { 
44589                 node.setAttribute(n, clean.join(';'));
44590             } else {
44591                 node.removeAttribute(n);
44592             }
44593             
44594         }
44595         
44596         
44597         for (var i = node.attributes.length-1; i > -1 ; i--) {
44598             var a = node.attributes[i];
44599             //console.log(a);
44600             
44601             if (a.name.toLowerCase().substr(0,2)=='on')  {
44602                 node.removeAttribute(a.name);
44603                 continue;
44604             }
44605             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44606                 node.removeAttribute(a.name);
44607                 continue;
44608             }
44609             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44610                 cleanAttr(a.name,a.value); // fixme..
44611                 continue;
44612             }
44613             if (a.name == 'style') {
44614                 cleanStyle(a.name,a.value);
44615                 continue;
44616             }
44617             /// clean up MS crap..
44618             // tecnically this should be a list of valid class'es..
44619             
44620             
44621             if (a.name == 'class') {
44622                 if (a.value.match(/^Mso/)) {
44623                     node.removeAttribute('class');
44624                 }
44625                 
44626                 if (a.value.match(/^body$/)) {
44627                     node.removeAttribute('class');
44628                 }
44629                 continue;
44630             }
44631             
44632             // style cleanup!?
44633             // class cleanup?
44634             
44635         }
44636         
44637         
44638         this.cleanUpChildren(node);
44639         
44640         
44641     },
44642     
44643     /**
44644      * Clean up MS wordisms...
44645      */
44646     cleanWord : function(node)
44647     {
44648         if (!node) {
44649             this.cleanWord(this.doc.body);
44650             return;
44651         }
44652         
44653         if(
44654                 node.nodeName == 'SPAN' &&
44655                 !node.hasAttributes() &&
44656                 node.childNodes.length == 1 &&
44657                 node.firstChild.nodeName == "#text"  
44658         ) {
44659             var textNode = node.firstChild;
44660             node.removeChild(textNode);
44661             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44662                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44663             }
44664             node.parentNode.insertBefore(textNode, node);
44665             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44666                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44667             }
44668             node.parentNode.removeChild(node);
44669         }
44670         
44671         if (node.nodeName == "#text") {
44672             // clean up silly Windows -- stuff?
44673             return; 
44674         }
44675         if (node.nodeName == "#comment") {
44676             node.parentNode.removeChild(node);
44677             // clean up silly Windows -- stuff?
44678             return; 
44679         }
44680         
44681         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44682             node.parentNode.removeChild(node);
44683             return;
44684         }
44685         //Roo.log(node.tagName);
44686         // remove - but keep children..
44687         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44688             //Roo.log('-- removed');
44689             while (node.childNodes.length) {
44690                 var cn = node.childNodes[0];
44691                 node.removeChild(cn);
44692                 node.parentNode.insertBefore(cn, node);
44693                 // move node to parent - and clean it..
44694                 this.cleanWord(cn);
44695             }
44696             node.parentNode.removeChild(node);
44697             /// no need to iterate chidlren = it's got none..
44698             //this.iterateChildren(node, this.cleanWord);
44699             return;
44700         }
44701         // clean styles
44702         if (node.className.length) {
44703             
44704             var cn = node.className.split(/\W+/);
44705             var cna = [];
44706             Roo.each(cn, function(cls) {
44707                 if (cls.match(/Mso[a-zA-Z]+/)) {
44708                     return;
44709                 }
44710                 cna.push(cls);
44711             });
44712             node.className = cna.length ? cna.join(' ') : '';
44713             if (!cna.length) {
44714                 node.removeAttribute("class");
44715             }
44716         }
44717         
44718         if (node.hasAttribute("lang")) {
44719             node.removeAttribute("lang");
44720         }
44721         
44722         if (node.hasAttribute("style")) {
44723             
44724             var styles = node.getAttribute("style").split(";");
44725             var nstyle = [];
44726             Roo.each(styles, function(s) {
44727                 if (!s.match(/:/)) {
44728                     return;
44729                 }
44730                 var kv = s.split(":");
44731                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44732                     return;
44733                 }
44734                 // what ever is left... we allow.
44735                 nstyle.push(s);
44736             });
44737             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44738             if (!nstyle.length) {
44739                 node.removeAttribute('style');
44740             }
44741         }
44742         this.iterateChildren(node, this.cleanWord);
44743         
44744         
44745         
44746     },
44747     /**
44748      * iterateChildren of a Node, calling fn each time, using this as the scole..
44749      * @param {DomNode} node node to iterate children of.
44750      * @param {Function} fn method of this class to call on each item.
44751      */
44752     iterateChildren : function(node, fn)
44753     {
44754         if (!node.childNodes.length) {
44755                 return;
44756         }
44757         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44758            fn.call(this, node.childNodes[i])
44759         }
44760     },
44761     
44762     
44763     /**
44764      * cleanTableWidths.
44765      *
44766      * Quite often pasting from word etc.. results in tables with column and widths.
44767      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44768      *
44769      */
44770     cleanTableWidths : function(node)
44771     {
44772          
44773          
44774         if (!node) {
44775             this.cleanTableWidths(this.doc.body);
44776             return;
44777         }
44778         
44779         // ignore list...
44780         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44781             return; 
44782         }
44783         Roo.log(node.tagName);
44784         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44785             this.iterateChildren(node, this.cleanTableWidths);
44786             return;
44787         }
44788         if (node.hasAttribute('width')) {
44789             node.removeAttribute('width');
44790         }
44791         
44792          
44793         if (node.hasAttribute("style")) {
44794             // pretty basic...
44795             
44796             var styles = node.getAttribute("style").split(";");
44797             var nstyle = [];
44798             Roo.each(styles, function(s) {
44799                 if (!s.match(/:/)) {
44800                     return;
44801                 }
44802                 var kv = s.split(":");
44803                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44804                     return;
44805                 }
44806                 // what ever is left... we allow.
44807                 nstyle.push(s);
44808             });
44809             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44810             if (!nstyle.length) {
44811                 node.removeAttribute('style');
44812             }
44813         }
44814         
44815         this.iterateChildren(node, this.cleanTableWidths);
44816         
44817         
44818     },
44819     
44820     
44821     
44822     
44823     domToHTML : function(currentElement, depth, nopadtext) {
44824         
44825         depth = depth || 0;
44826         nopadtext = nopadtext || false;
44827     
44828         if (!currentElement) {
44829             return this.domToHTML(this.doc.body);
44830         }
44831         
44832         //Roo.log(currentElement);
44833         var j;
44834         var allText = false;
44835         var nodeName = currentElement.nodeName;
44836         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44837         
44838         if  (nodeName == '#text') {
44839             
44840             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44841         }
44842         
44843         
44844         var ret = '';
44845         if (nodeName != 'BODY') {
44846              
44847             var i = 0;
44848             // Prints the node tagName, such as <A>, <IMG>, etc
44849             if (tagName) {
44850                 var attr = [];
44851                 for(i = 0; i < currentElement.attributes.length;i++) {
44852                     // quoting?
44853                     var aname = currentElement.attributes.item(i).name;
44854                     if (!currentElement.attributes.item(i).value.length) {
44855                         continue;
44856                     }
44857                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44858                 }
44859                 
44860                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44861             } 
44862             else {
44863                 
44864                 // eack
44865             }
44866         } else {
44867             tagName = false;
44868         }
44869         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44870             return ret;
44871         }
44872         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44873             nopadtext = true;
44874         }
44875         
44876         
44877         // Traverse the tree
44878         i = 0;
44879         var currentElementChild = currentElement.childNodes.item(i);
44880         var allText = true;
44881         var innerHTML  = '';
44882         lastnode = '';
44883         while (currentElementChild) {
44884             // Formatting code (indent the tree so it looks nice on the screen)
44885             var nopad = nopadtext;
44886             if (lastnode == 'SPAN') {
44887                 nopad  = true;
44888             }
44889             // text
44890             if  (currentElementChild.nodeName == '#text') {
44891                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44892                 toadd = nopadtext ? toadd : toadd.trim();
44893                 if (!nopad && toadd.length > 80) {
44894                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44895                 }
44896                 innerHTML  += toadd;
44897                 
44898                 i++;
44899                 currentElementChild = currentElement.childNodes.item(i);
44900                 lastNode = '';
44901                 continue;
44902             }
44903             allText = false;
44904             
44905             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44906                 
44907             // Recursively traverse the tree structure of the child node
44908             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44909             lastnode = currentElementChild.nodeName;
44910             i++;
44911             currentElementChild=currentElement.childNodes.item(i);
44912         }
44913         
44914         ret += innerHTML;
44915         
44916         if (!allText) {
44917                 // The remaining code is mostly for formatting the tree
44918             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44919         }
44920         
44921         
44922         if (tagName) {
44923             ret+= "</"+tagName+">";
44924         }
44925         return ret;
44926         
44927     },
44928         
44929     applyBlacklists : function()
44930     {
44931         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44932         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44933         
44934         this.white = [];
44935         this.black = [];
44936         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44937             if (b.indexOf(tag) > -1) {
44938                 return;
44939             }
44940             this.white.push(tag);
44941             
44942         }, this);
44943         
44944         Roo.each(w, function(tag) {
44945             if (b.indexOf(tag) > -1) {
44946                 return;
44947             }
44948             if (this.white.indexOf(tag) > -1) {
44949                 return;
44950             }
44951             this.white.push(tag);
44952             
44953         }, this);
44954         
44955         
44956         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44957             if (w.indexOf(tag) > -1) {
44958                 return;
44959             }
44960             this.black.push(tag);
44961             
44962         }, this);
44963         
44964         Roo.each(b, function(tag) {
44965             if (w.indexOf(tag) > -1) {
44966                 return;
44967             }
44968             if (this.black.indexOf(tag) > -1) {
44969                 return;
44970             }
44971             this.black.push(tag);
44972             
44973         }, this);
44974         
44975         
44976         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44977         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44978         
44979         this.cwhite = [];
44980         this.cblack = [];
44981         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44982             if (b.indexOf(tag) > -1) {
44983                 return;
44984             }
44985             this.cwhite.push(tag);
44986             
44987         }, this);
44988         
44989         Roo.each(w, function(tag) {
44990             if (b.indexOf(tag) > -1) {
44991                 return;
44992             }
44993             if (this.cwhite.indexOf(tag) > -1) {
44994                 return;
44995             }
44996             this.cwhite.push(tag);
44997             
44998         }, this);
44999         
45000         
45001         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45002             if (w.indexOf(tag) > -1) {
45003                 return;
45004             }
45005             this.cblack.push(tag);
45006             
45007         }, this);
45008         
45009         Roo.each(b, function(tag) {
45010             if (w.indexOf(tag) > -1) {
45011                 return;
45012             }
45013             if (this.cblack.indexOf(tag) > -1) {
45014                 return;
45015             }
45016             this.cblack.push(tag);
45017             
45018         }, this);
45019     },
45020     
45021     setStylesheets : function(stylesheets)
45022     {
45023         if(typeof(stylesheets) == 'string'){
45024             Roo.get(this.iframe.contentDocument.head).createChild({
45025                 tag : 'link',
45026                 rel : 'stylesheet',
45027                 type : 'text/css',
45028                 href : stylesheets
45029             });
45030             
45031             return;
45032         }
45033         var _this = this;
45034      
45035         Roo.each(stylesheets, function(s) {
45036             if(!s.length){
45037                 return;
45038             }
45039             
45040             Roo.get(_this.iframe.contentDocument.head).createChild({
45041                 tag : 'link',
45042                 rel : 'stylesheet',
45043                 type : 'text/css',
45044                 href : s
45045             });
45046         });
45047
45048         
45049     },
45050     
45051     removeStylesheets : function()
45052     {
45053         var _this = this;
45054         
45055         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45056             s.remove();
45057         });
45058     },
45059     
45060     setStyle : function(style)
45061     {
45062         Roo.get(this.iframe.contentDocument.head).createChild({
45063             tag : 'style',
45064             type : 'text/css',
45065             html : style
45066         });
45067
45068         return;
45069     }
45070     
45071     // hide stuff that is not compatible
45072     /**
45073      * @event blur
45074      * @hide
45075      */
45076     /**
45077      * @event change
45078      * @hide
45079      */
45080     /**
45081      * @event focus
45082      * @hide
45083      */
45084     /**
45085      * @event specialkey
45086      * @hide
45087      */
45088     /**
45089      * @cfg {String} fieldClass @hide
45090      */
45091     /**
45092      * @cfg {String} focusClass @hide
45093      */
45094     /**
45095      * @cfg {String} autoCreate @hide
45096      */
45097     /**
45098      * @cfg {String} inputType @hide
45099      */
45100     /**
45101      * @cfg {String} invalidClass @hide
45102      */
45103     /**
45104      * @cfg {String} invalidText @hide
45105      */
45106     /**
45107      * @cfg {String} msgFx @hide
45108      */
45109     /**
45110      * @cfg {String} validateOnBlur @hide
45111      */
45112 });
45113
45114 Roo.HtmlEditorCore.white = [
45115         'area', 'br', 'img', 'input', 'hr', 'wbr',
45116         
45117        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45118        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45119        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45120        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45121        'table',   'ul',         'xmp', 
45122        
45123        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45124       'thead',   'tr', 
45125      
45126       'dir', 'menu', 'ol', 'ul', 'dl',
45127        
45128       'embed',  'object'
45129 ];
45130
45131
45132 Roo.HtmlEditorCore.black = [
45133     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45134         'applet', // 
45135         'base',   'basefont', 'bgsound', 'blink',  'body', 
45136         'frame',  'frameset', 'head',    'html',   'ilayer', 
45137         'iframe', 'layer',  'link',     'meta',    'object',   
45138         'script', 'style' ,'title',  'xml' // clean later..
45139 ];
45140 Roo.HtmlEditorCore.clean = [
45141     'script', 'style', 'title', 'xml'
45142 ];
45143 Roo.HtmlEditorCore.remove = [
45144     'font'
45145 ];
45146 // attributes..
45147
45148 Roo.HtmlEditorCore.ablack = [
45149     'on'
45150 ];
45151     
45152 Roo.HtmlEditorCore.aclean = [ 
45153     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45154 ];
45155
45156 // protocols..
45157 Roo.HtmlEditorCore.pwhite= [
45158         'http',  'https',  'mailto'
45159 ];
45160
45161 // white listed style attributes.
45162 Roo.HtmlEditorCore.cwhite= [
45163       //  'text-align', /// default is to allow most things..
45164       
45165          
45166 //        'font-size'//??
45167 ];
45168
45169 // black listed style attributes.
45170 Roo.HtmlEditorCore.cblack= [
45171       //  'font-size' -- this can be set by the project 
45172 ];
45173
45174
45175 Roo.HtmlEditorCore.swapCodes   =[ 
45176     [    8211, "--" ], 
45177     [    8212, "--" ], 
45178     [    8216,  "'" ],  
45179     [    8217, "'" ],  
45180     [    8220, '"' ],  
45181     [    8221, '"' ],  
45182     [    8226, "*" ],  
45183     [    8230, "..." ]
45184 ]; 
45185
45186     //<script type="text/javascript">
45187
45188 /*
45189  * Ext JS Library 1.1.1
45190  * Copyright(c) 2006-2007, Ext JS, LLC.
45191  * Licence LGPL
45192  * 
45193  */
45194  
45195  
45196 Roo.form.HtmlEditor = function(config){
45197     
45198     
45199     
45200     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45201     
45202     if (!this.toolbars) {
45203         this.toolbars = [];
45204     }
45205     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45206     
45207     
45208 };
45209
45210 /**
45211  * @class Roo.form.HtmlEditor
45212  * @extends Roo.form.Field
45213  * Provides a lightweight HTML Editor component.
45214  *
45215  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45216  * 
45217  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45218  * supported by this editor.</b><br/><br/>
45219  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45220  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45221  */
45222 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45223     /**
45224      * @cfg {Boolean} clearUp
45225      */
45226     clearUp : true,
45227       /**
45228      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45229      */
45230     toolbars : false,
45231    
45232      /**
45233      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45234      *                        Roo.resizable.
45235      */
45236     resizable : false,
45237      /**
45238      * @cfg {Number} height (in pixels)
45239      */   
45240     height: 300,
45241    /**
45242      * @cfg {Number} width (in pixels)
45243      */   
45244     width: 500,
45245     
45246     /**
45247      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45248      * 
45249      */
45250     stylesheets: false,
45251     
45252     
45253      /**
45254      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45255      * 
45256      */
45257     cblack: false,
45258     /**
45259      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45260      * 
45261      */
45262     cwhite: false,
45263     
45264      /**
45265      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45266      * 
45267      */
45268     black: false,
45269     /**
45270      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45271      * 
45272      */
45273     white: false,
45274     
45275     // id of frame..
45276     frameId: false,
45277     
45278     // private properties
45279     validationEvent : false,
45280     deferHeight: true,
45281     initialized : false,
45282     activated : false,
45283     
45284     onFocus : Roo.emptyFn,
45285     iframePad:3,
45286     hideMode:'offsets',
45287     
45288     actionMode : 'container', // defaults to hiding it...
45289     
45290     defaultAutoCreate : { // modified by initCompnoent..
45291         tag: "textarea",
45292         style:"width:500px;height:300px;",
45293         autocomplete: "new-password"
45294     },
45295
45296     // private
45297     initComponent : function(){
45298         this.addEvents({
45299             /**
45300              * @event initialize
45301              * Fires when the editor is fully initialized (including the iframe)
45302              * @param {HtmlEditor} this
45303              */
45304             initialize: true,
45305             /**
45306              * @event activate
45307              * Fires when the editor is first receives the focus. Any insertion must wait
45308              * until after this event.
45309              * @param {HtmlEditor} this
45310              */
45311             activate: true,
45312              /**
45313              * @event beforesync
45314              * Fires before the textarea is updated with content from the editor iframe. Return false
45315              * to cancel the sync.
45316              * @param {HtmlEditor} this
45317              * @param {String} html
45318              */
45319             beforesync: true,
45320              /**
45321              * @event beforepush
45322              * Fires before the iframe editor is updated with content from the textarea. Return false
45323              * to cancel the push.
45324              * @param {HtmlEditor} this
45325              * @param {String} html
45326              */
45327             beforepush: true,
45328              /**
45329              * @event sync
45330              * Fires when the textarea is updated with content from the editor iframe.
45331              * @param {HtmlEditor} this
45332              * @param {String} html
45333              */
45334             sync: true,
45335              /**
45336              * @event push
45337              * Fires when the iframe editor is updated with content from the textarea.
45338              * @param {HtmlEditor} this
45339              * @param {String} html
45340              */
45341             push: true,
45342              /**
45343              * @event editmodechange
45344              * Fires when the editor switches edit modes
45345              * @param {HtmlEditor} this
45346              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45347              */
45348             editmodechange: true,
45349             /**
45350              * @event editorevent
45351              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45352              * @param {HtmlEditor} this
45353              */
45354             editorevent: true,
45355             /**
45356              * @event firstfocus
45357              * Fires when on first focus - needed by toolbars..
45358              * @param {HtmlEditor} this
45359              */
45360             firstfocus: true,
45361             /**
45362              * @event autosave
45363              * Auto save the htmlEditor value as a file into Events
45364              * @param {HtmlEditor} this
45365              */
45366             autosave: true,
45367             /**
45368              * @event savedpreview
45369              * preview the saved version of htmlEditor
45370              * @param {HtmlEditor} this
45371              */
45372             savedpreview: true,
45373             
45374             /**
45375             * @event stylesheetsclick
45376             * Fires when press the Sytlesheets button
45377             * @param {Roo.HtmlEditorCore} this
45378             */
45379             stylesheetsclick: true
45380         });
45381         this.defaultAutoCreate =  {
45382             tag: "textarea",
45383             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45384             autocomplete: "new-password"
45385         };
45386     },
45387
45388     /**
45389      * Protected method that will not generally be called directly. It
45390      * is called when the editor creates its toolbar. Override this method if you need to
45391      * add custom toolbar buttons.
45392      * @param {HtmlEditor} editor
45393      */
45394     createToolbar : function(editor){
45395         Roo.log("create toolbars");
45396         if (!editor.toolbars || !editor.toolbars.length) {
45397             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45398         }
45399         
45400         for (var i =0 ; i < editor.toolbars.length;i++) {
45401             editor.toolbars[i] = Roo.factory(
45402                     typeof(editor.toolbars[i]) == 'string' ?
45403                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45404                 Roo.form.HtmlEditor);
45405             editor.toolbars[i].init(editor);
45406         }
45407          
45408         
45409     },
45410
45411      
45412     // private
45413     onRender : function(ct, position)
45414     {
45415         var _t = this;
45416         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45417         
45418         this.wrap = this.el.wrap({
45419             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45420         });
45421         
45422         this.editorcore.onRender(ct, position);
45423          
45424         if (this.resizable) {
45425             this.resizeEl = new Roo.Resizable(this.wrap, {
45426                 pinned : true,
45427                 wrap: true,
45428                 dynamic : true,
45429                 minHeight : this.height,
45430                 height: this.height,
45431                 handles : this.resizable,
45432                 width: this.width,
45433                 listeners : {
45434                     resize : function(r, w, h) {
45435                         _t.onResize(w,h); // -something
45436                     }
45437                 }
45438             });
45439             
45440         }
45441         this.createToolbar(this);
45442        
45443         
45444         if(!this.width){
45445             this.setSize(this.wrap.getSize());
45446         }
45447         if (this.resizeEl) {
45448             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45449             // should trigger onReize..
45450         }
45451         
45452         this.keyNav = new Roo.KeyNav(this.el, {
45453             
45454             "tab" : function(e){
45455                 e.preventDefault();
45456                 
45457                 var value = this.getValue();
45458                 
45459                 var start = this.el.dom.selectionStart;
45460                 var end = this.el.dom.selectionEnd;
45461                 
45462                 if(!e.shiftKey){
45463                     
45464                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45465                     this.el.dom.setSelectionRange(end + 1, end + 1);
45466                     return;
45467                 }
45468                 
45469                 var f = value.substring(0, start).split("\t");
45470                 
45471                 if(f.pop().length != 0){
45472                     return;
45473                 }
45474                 
45475                 this.setValue(f.join("\t") + value.substring(end));
45476                 this.el.dom.setSelectionRange(start - 1, start - 1);
45477                 
45478             },
45479             
45480             "home" : function(e){
45481                 e.preventDefault();
45482                 
45483                 var curr = this.el.dom.selectionStart;
45484                 var lines = this.getValue().split("\n");
45485                 
45486                 if(!lines.length){
45487                     return;
45488                 }
45489                 
45490                 if(e.ctrlKey){
45491                     this.el.dom.setSelectionRange(0, 0);
45492                     return;
45493                 }
45494                 
45495                 var pos = 0;
45496                 
45497                 for (var i = 0; i < lines.length;i++) {
45498                     pos += lines[i].length;
45499                     
45500                     if(i != 0){
45501                         pos += 1;
45502                     }
45503                     
45504                     if(pos < curr){
45505                         continue;
45506                     }
45507                     
45508                     pos -= lines[i].length;
45509                     
45510                     break;
45511                 }
45512                 
45513                 if(!e.shiftKey){
45514                     this.el.dom.setSelectionRange(pos, pos);
45515                     return;
45516                 }
45517                 
45518                 this.el.dom.selectionStart = pos;
45519                 this.el.dom.selectionEnd = curr;
45520             },
45521             
45522             "end" : function(e){
45523                 e.preventDefault();
45524                 
45525                 var curr = this.el.dom.selectionStart;
45526                 var lines = this.getValue().split("\n");
45527                 
45528                 if(!lines.length){
45529                     return;
45530                 }
45531                 
45532                 if(e.ctrlKey){
45533                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45534                     return;
45535                 }
45536                 
45537                 var pos = 0;
45538                 
45539                 for (var i = 0; i < lines.length;i++) {
45540                     
45541                     pos += lines[i].length;
45542                     
45543                     if(i != 0){
45544                         pos += 1;
45545                     }
45546                     
45547                     if(pos < curr){
45548                         continue;
45549                     }
45550                     
45551                     break;
45552                 }
45553                 
45554                 if(!e.shiftKey){
45555                     this.el.dom.setSelectionRange(pos, pos);
45556                     return;
45557                 }
45558                 
45559                 this.el.dom.selectionStart = curr;
45560                 this.el.dom.selectionEnd = pos;
45561             },
45562
45563             scope : this,
45564
45565             doRelay : function(foo, bar, hname){
45566                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45567             },
45568
45569             forceKeyDown: true
45570         });
45571         
45572 //        if(this.autosave && this.w){
45573 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45574 //        }
45575     },
45576
45577     // private
45578     onResize : function(w, h)
45579     {
45580         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45581         var ew = false;
45582         var eh = false;
45583         
45584         if(this.el ){
45585             if(typeof w == 'number'){
45586                 var aw = w - this.wrap.getFrameWidth('lr');
45587                 this.el.setWidth(this.adjustWidth('textarea', aw));
45588                 ew = aw;
45589             }
45590             if(typeof h == 'number'){
45591                 var tbh = 0;
45592                 for (var i =0; i < this.toolbars.length;i++) {
45593                     // fixme - ask toolbars for heights?
45594                     tbh += this.toolbars[i].tb.el.getHeight();
45595                     if (this.toolbars[i].footer) {
45596                         tbh += this.toolbars[i].footer.el.getHeight();
45597                     }
45598                 }
45599                 
45600                 
45601                 
45602                 
45603                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45604                 ah -= 5; // knock a few pixes off for look..
45605 //                Roo.log(ah);
45606                 this.el.setHeight(this.adjustWidth('textarea', ah));
45607                 var eh = ah;
45608             }
45609         }
45610         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45611         this.editorcore.onResize(ew,eh);
45612         
45613     },
45614
45615     /**
45616      * Toggles the editor between standard and source edit mode.
45617      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45618      */
45619     toggleSourceEdit : function(sourceEditMode)
45620     {
45621         this.editorcore.toggleSourceEdit(sourceEditMode);
45622         
45623         if(this.editorcore.sourceEditMode){
45624             Roo.log('editor - showing textarea');
45625             
45626 //            Roo.log('in');
45627 //            Roo.log(this.syncValue());
45628             this.editorcore.syncValue();
45629             this.el.removeClass('x-hidden');
45630             this.el.dom.removeAttribute('tabIndex');
45631             this.el.focus();
45632             
45633             for (var i = 0; i < this.toolbars.length; i++) {
45634                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45635                     this.toolbars[i].tb.hide();
45636                     this.toolbars[i].footer.hide();
45637                 }
45638             }
45639             
45640         }else{
45641             Roo.log('editor - hiding textarea');
45642 //            Roo.log('out')
45643 //            Roo.log(this.pushValue()); 
45644             this.editorcore.pushValue();
45645             
45646             this.el.addClass('x-hidden');
45647             this.el.dom.setAttribute('tabIndex', -1);
45648             
45649             for (var i = 0; i < this.toolbars.length; i++) {
45650                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45651                     this.toolbars[i].tb.show();
45652                     this.toolbars[i].footer.show();
45653                 }
45654             }
45655             
45656             //this.deferFocus();
45657         }
45658         
45659         this.setSize(this.wrap.getSize());
45660         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45661         
45662         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45663     },
45664  
45665     // private (for BoxComponent)
45666     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45667
45668     // private (for BoxComponent)
45669     getResizeEl : function(){
45670         return this.wrap;
45671     },
45672
45673     // private (for BoxComponent)
45674     getPositionEl : function(){
45675         return this.wrap;
45676     },
45677
45678     // private
45679     initEvents : function(){
45680         this.originalValue = this.getValue();
45681     },
45682
45683     /**
45684      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45685      * @method
45686      */
45687     markInvalid : Roo.emptyFn,
45688     /**
45689      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45690      * @method
45691      */
45692     clearInvalid : Roo.emptyFn,
45693
45694     setValue : function(v){
45695         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45696         this.editorcore.pushValue();
45697     },
45698
45699      
45700     // private
45701     deferFocus : function(){
45702         this.focus.defer(10, this);
45703     },
45704
45705     // doc'ed in Field
45706     focus : function(){
45707         this.editorcore.focus();
45708         
45709     },
45710       
45711
45712     // private
45713     onDestroy : function(){
45714         
45715         
45716         
45717         if(this.rendered){
45718             
45719             for (var i =0; i < this.toolbars.length;i++) {
45720                 // fixme - ask toolbars for heights?
45721                 this.toolbars[i].onDestroy();
45722             }
45723             
45724             this.wrap.dom.innerHTML = '';
45725             this.wrap.remove();
45726         }
45727     },
45728
45729     // private
45730     onFirstFocus : function(){
45731         //Roo.log("onFirstFocus");
45732         this.editorcore.onFirstFocus();
45733          for (var i =0; i < this.toolbars.length;i++) {
45734             this.toolbars[i].onFirstFocus();
45735         }
45736         
45737     },
45738     
45739     // private
45740     syncValue : function()
45741     {
45742         this.editorcore.syncValue();
45743     },
45744     
45745     pushValue : function()
45746     {
45747         this.editorcore.pushValue();
45748     },
45749     
45750     setStylesheets : function(stylesheets)
45751     {
45752         this.editorcore.setStylesheets(stylesheets);
45753     },
45754     
45755     removeStylesheets : function()
45756     {
45757         this.editorcore.removeStylesheets();
45758     }
45759      
45760     
45761     // hide stuff that is not compatible
45762     /**
45763      * @event blur
45764      * @hide
45765      */
45766     /**
45767      * @event change
45768      * @hide
45769      */
45770     /**
45771      * @event focus
45772      * @hide
45773      */
45774     /**
45775      * @event specialkey
45776      * @hide
45777      */
45778     /**
45779      * @cfg {String} fieldClass @hide
45780      */
45781     /**
45782      * @cfg {String} focusClass @hide
45783      */
45784     /**
45785      * @cfg {String} autoCreate @hide
45786      */
45787     /**
45788      * @cfg {String} inputType @hide
45789      */
45790     /**
45791      * @cfg {String} invalidClass @hide
45792      */
45793     /**
45794      * @cfg {String} invalidText @hide
45795      */
45796     /**
45797      * @cfg {String} msgFx @hide
45798      */
45799     /**
45800      * @cfg {String} validateOnBlur @hide
45801      */
45802 });
45803  
45804     // <script type="text/javascript">
45805 /*
45806  * Based on
45807  * Ext JS Library 1.1.1
45808  * Copyright(c) 2006-2007, Ext JS, LLC.
45809  *  
45810  
45811  */
45812
45813 /**
45814  * @class Roo.form.HtmlEditorToolbar1
45815  * Basic Toolbar
45816  * 
45817  * Usage:
45818  *
45819  new Roo.form.HtmlEditor({
45820     ....
45821     toolbars : [
45822         new Roo.form.HtmlEditorToolbar1({
45823             disable : { fonts: 1 , format: 1, ..., ... , ...],
45824             btns : [ .... ]
45825         })
45826     }
45827      
45828  * 
45829  * @cfg {Object} disable List of elements to disable..
45830  * @cfg {Array} btns List of additional buttons.
45831  * 
45832  * 
45833  * NEEDS Extra CSS? 
45834  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45835  */
45836  
45837 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45838 {
45839     
45840     Roo.apply(this, config);
45841     
45842     // default disabled, based on 'good practice'..
45843     this.disable = this.disable || {};
45844     Roo.applyIf(this.disable, {
45845         fontSize : true,
45846         colors : true,
45847         specialElements : true
45848     });
45849     
45850     
45851     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45852     // dont call parent... till later.
45853 }
45854
45855 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45856     
45857     tb: false,
45858     
45859     rendered: false,
45860     
45861     editor : false,
45862     editorcore : false,
45863     /**
45864      * @cfg {Object} disable  List of toolbar elements to disable
45865          
45866      */
45867     disable : false,
45868     
45869     
45870      /**
45871      * @cfg {String} createLinkText The default text for the create link prompt
45872      */
45873     createLinkText : 'Please enter the URL for the link:',
45874     /**
45875      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45876      */
45877     defaultLinkValue : 'http:/'+'/',
45878    
45879     
45880       /**
45881      * @cfg {Array} fontFamilies An array of available font families
45882      */
45883     fontFamilies : [
45884         'Arial',
45885         'Courier New',
45886         'Tahoma',
45887         'Times New Roman',
45888         'Verdana'
45889     ],
45890     
45891     specialChars : [
45892            "&#169;",
45893           "&#174;",     
45894           "&#8482;",    
45895           "&#163;" ,    
45896          // "&#8212;",    
45897           "&#8230;",    
45898           "&#247;" ,    
45899         //  "&#225;" ,     ?? a acute?
45900            "&#8364;"    , //Euro
45901        //   "&#8220;"    ,
45902         //  "&#8221;"    ,
45903         //  "&#8226;"    ,
45904           "&#176;"  //   , // degrees
45905
45906          // "&#233;"     , // e ecute
45907          // "&#250;"     , // u ecute?
45908     ],
45909     
45910     specialElements : [
45911         {
45912             text: "Insert Table",
45913             xtype: 'MenuItem',
45914             xns : Roo.Menu,
45915             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45916                 
45917         },
45918         {    
45919             text: "Insert Image",
45920             xtype: 'MenuItem',
45921             xns : Roo.Menu,
45922             ihtml : '<img src="about:blank"/>'
45923             
45924         }
45925         
45926          
45927     ],
45928     
45929     
45930     inputElements : [ 
45931             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45932             "input:submit", "input:button", "select", "textarea", "label" ],
45933     formats : [
45934         ["p"] ,  
45935         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45936         ["pre"],[ "code"], 
45937         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45938         ['div'],['span'],
45939         ['sup'],['sub']
45940     ],
45941     
45942     cleanStyles : [
45943         "font-size"
45944     ],
45945      /**
45946      * @cfg {String} defaultFont default font to use.
45947      */
45948     defaultFont: 'tahoma',
45949    
45950     fontSelect : false,
45951     
45952     
45953     formatCombo : false,
45954     
45955     init : function(editor)
45956     {
45957         this.editor = editor;
45958         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45959         var editorcore = this.editorcore;
45960         
45961         var _t = this;
45962         
45963         var fid = editorcore.frameId;
45964         var etb = this;
45965         function btn(id, toggle, handler){
45966             var xid = fid + '-'+ id ;
45967             return {
45968                 id : xid,
45969                 cmd : id,
45970                 cls : 'x-btn-icon x-edit-'+id,
45971                 enableToggle:toggle !== false,
45972                 scope: _t, // was editor...
45973                 handler:handler||_t.relayBtnCmd,
45974                 clickEvent:'mousedown',
45975                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45976                 tabIndex:-1
45977             };
45978         }
45979         
45980         
45981         
45982         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45983         this.tb = tb;
45984          // stop form submits
45985         tb.el.on('click', function(e){
45986             e.preventDefault(); // what does this do?
45987         });
45988
45989         if(!this.disable.font) { // && !Roo.isSafari){
45990             /* why no safari for fonts 
45991             editor.fontSelect = tb.el.createChild({
45992                 tag:'select',
45993                 tabIndex: -1,
45994                 cls:'x-font-select',
45995                 html: this.createFontOptions()
45996             });
45997             
45998             editor.fontSelect.on('change', function(){
45999                 var font = editor.fontSelect.dom.value;
46000                 editor.relayCmd('fontname', font);
46001                 editor.deferFocus();
46002             }, editor);
46003             
46004             tb.add(
46005                 editor.fontSelect.dom,
46006                 '-'
46007             );
46008             */
46009             
46010         };
46011         if(!this.disable.formats){
46012             this.formatCombo = new Roo.form.ComboBox({
46013                 store: new Roo.data.SimpleStore({
46014                     id : 'tag',
46015                     fields: ['tag'],
46016                     data : this.formats // from states.js
46017                 }),
46018                 blockFocus : true,
46019                 name : '',
46020                 //autoCreate : {tag: "div",  size: "20"},
46021                 displayField:'tag',
46022                 typeAhead: false,
46023                 mode: 'local',
46024                 editable : false,
46025                 triggerAction: 'all',
46026                 emptyText:'Add tag',
46027                 selectOnFocus:true,
46028                 width:135,
46029                 listeners : {
46030                     'select': function(c, r, i) {
46031                         editorcore.insertTag(r.get('tag'));
46032                         editor.focus();
46033                     }
46034                 }
46035
46036             });
46037             tb.addField(this.formatCombo);
46038             
46039         }
46040         
46041         if(!this.disable.format){
46042             tb.add(
46043                 btn('bold'),
46044                 btn('italic'),
46045                 btn('underline'),
46046                 btn('strikethrough')
46047             );
46048         };
46049         if(!this.disable.fontSize){
46050             tb.add(
46051                 '-',
46052                 
46053                 
46054                 btn('increasefontsize', false, editorcore.adjustFont),
46055                 btn('decreasefontsize', false, editorcore.adjustFont)
46056             );
46057         };
46058         
46059         
46060         if(!this.disable.colors){
46061             tb.add(
46062                 '-', {
46063                     id:editorcore.frameId +'-forecolor',
46064                     cls:'x-btn-icon x-edit-forecolor',
46065                     clickEvent:'mousedown',
46066                     tooltip: this.buttonTips['forecolor'] || undefined,
46067                     tabIndex:-1,
46068                     menu : new Roo.menu.ColorMenu({
46069                         allowReselect: true,
46070                         focus: Roo.emptyFn,
46071                         value:'000000',
46072                         plain:true,
46073                         selectHandler: function(cp, color){
46074                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46075                             editor.deferFocus();
46076                         },
46077                         scope: editorcore,
46078                         clickEvent:'mousedown'
46079                     })
46080                 }, {
46081                     id:editorcore.frameId +'backcolor',
46082                     cls:'x-btn-icon x-edit-backcolor',
46083                     clickEvent:'mousedown',
46084                     tooltip: this.buttonTips['backcolor'] || undefined,
46085                     tabIndex:-1,
46086                     menu : new Roo.menu.ColorMenu({
46087                         focus: Roo.emptyFn,
46088                         value:'FFFFFF',
46089                         plain:true,
46090                         allowReselect: true,
46091                         selectHandler: function(cp, color){
46092                             if(Roo.isGecko){
46093                                 editorcore.execCmd('useCSS', false);
46094                                 editorcore.execCmd('hilitecolor', color);
46095                                 editorcore.execCmd('useCSS', true);
46096                                 editor.deferFocus();
46097                             }else{
46098                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46099                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46100                                 editor.deferFocus();
46101                             }
46102                         },
46103                         scope:editorcore,
46104                         clickEvent:'mousedown'
46105                     })
46106                 }
46107             );
46108         };
46109         // now add all the items...
46110         
46111
46112         if(!this.disable.alignments){
46113             tb.add(
46114                 '-',
46115                 btn('justifyleft'),
46116                 btn('justifycenter'),
46117                 btn('justifyright')
46118             );
46119         };
46120
46121         //if(!Roo.isSafari){
46122             if(!this.disable.links){
46123                 tb.add(
46124                     '-',
46125                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46126                 );
46127             };
46128
46129             if(!this.disable.lists){
46130                 tb.add(
46131                     '-',
46132                     btn('insertorderedlist'),
46133                     btn('insertunorderedlist')
46134                 );
46135             }
46136             if(!this.disable.sourceEdit){
46137                 tb.add(
46138                     '-',
46139                     btn('sourceedit', true, function(btn){
46140                         this.toggleSourceEdit(btn.pressed);
46141                     })
46142                 );
46143             }
46144         //}
46145         
46146         var smenu = { };
46147         // special menu.. - needs to be tidied up..
46148         if (!this.disable.special) {
46149             smenu = {
46150                 text: "&#169;",
46151                 cls: 'x-edit-none',
46152                 
46153                 menu : {
46154                     items : []
46155                 }
46156             };
46157             for (var i =0; i < this.specialChars.length; i++) {
46158                 smenu.menu.items.push({
46159                     
46160                     html: this.specialChars[i],
46161                     handler: function(a,b) {
46162                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46163                         //editor.insertAtCursor(a.html);
46164                         
46165                     },
46166                     tabIndex:-1
46167                 });
46168             }
46169             
46170             
46171             tb.add(smenu);
46172             
46173             
46174         }
46175         
46176         var cmenu = { };
46177         if (!this.disable.cleanStyles) {
46178             cmenu = {
46179                 cls: 'x-btn-icon x-btn-clear',
46180                 
46181                 menu : {
46182                     items : []
46183                 }
46184             };
46185             for (var i =0; i < this.cleanStyles.length; i++) {
46186                 cmenu.menu.items.push({
46187                     actiontype : this.cleanStyles[i],
46188                     html: 'Remove ' + this.cleanStyles[i],
46189                     handler: function(a,b) {
46190 //                        Roo.log(a);
46191 //                        Roo.log(b);
46192                         var c = Roo.get(editorcore.doc.body);
46193                         c.select('[style]').each(function(s) {
46194                             s.dom.style.removeProperty(a.actiontype);
46195                         });
46196                         editorcore.syncValue();
46197                     },
46198                     tabIndex:-1
46199                 });
46200             }
46201              cmenu.menu.items.push({
46202                 actiontype : 'tablewidths',
46203                 html: 'Remove Table Widths',
46204                 handler: function(a,b) {
46205                     editorcore.cleanTableWidths();
46206                     editorcore.syncValue();
46207                 },
46208                 tabIndex:-1
46209             });
46210             cmenu.menu.items.push({
46211                 actiontype : 'word',
46212                 html: 'Remove MS Word Formating',
46213                 handler: function(a,b) {
46214                     editorcore.cleanWord();
46215                     editorcore.syncValue();
46216                 },
46217                 tabIndex:-1
46218             });
46219             
46220             cmenu.menu.items.push({
46221                 actiontype : 'all',
46222                 html: 'Remove All Styles',
46223                 handler: function(a,b) {
46224                     
46225                     var c = Roo.get(editorcore.doc.body);
46226                     c.select('[style]').each(function(s) {
46227                         s.dom.removeAttribute('style');
46228                     });
46229                     editorcore.syncValue();
46230                 },
46231                 tabIndex:-1
46232             });
46233             
46234             cmenu.menu.items.push({
46235                 actiontype : 'all',
46236                 html: 'Remove All CSS Classes',
46237                 handler: function(a,b) {
46238                     
46239                     var c = Roo.get(editorcore.doc.body);
46240                     c.select('[class]').each(function(s) {
46241                         s.dom.removeAttribute('class');
46242                     });
46243                     editorcore.cleanWord();
46244                     editorcore.syncValue();
46245                 },
46246                 tabIndex:-1
46247             });
46248             
46249              cmenu.menu.items.push({
46250                 actiontype : 'tidy',
46251                 html: 'Tidy HTML Source',
46252                 handler: function(a,b) {
46253                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46254                     editorcore.syncValue();
46255                 },
46256                 tabIndex:-1
46257             });
46258             
46259             
46260             tb.add(cmenu);
46261         }
46262          
46263         if (!this.disable.specialElements) {
46264             var semenu = {
46265                 text: "Other;",
46266                 cls: 'x-edit-none',
46267                 menu : {
46268                     items : []
46269                 }
46270             };
46271             for (var i =0; i < this.specialElements.length; i++) {
46272                 semenu.menu.items.push(
46273                     Roo.apply({ 
46274                         handler: function(a,b) {
46275                             editor.insertAtCursor(this.ihtml);
46276                         }
46277                     }, this.specialElements[i])
46278                 );
46279                     
46280             }
46281             
46282             tb.add(semenu);
46283             
46284             
46285         }
46286          
46287         
46288         if (this.btns) {
46289             for(var i =0; i< this.btns.length;i++) {
46290                 var b = Roo.factory(this.btns[i],Roo.form);
46291                 b.cls =  'x-edit-none';
46292                 
46293                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46294                     b.cls += ' x-init-enable';
46295                 }
46296                 
46297                 b.scope = editorcore;
46298                 tb.add(b);
46299             }
46300         
46301         }
46302         
46303         
46304         
46305         // disable everything...
46306         
46307         this.tb.items.each(function(item){
46308             
46309            if(
46310                 item.id != editorcore.frameId+ '-sourceedit' && 
46311                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46312             ){
46313                 
46314                 item.disable();
46315             }
46316         });
46317         this.rendered = true;
46318         
46319         // the all the btns;
46320         editor.on('editorevent', this.updateToolbar, this);
46321         // other toolbars need to implement this..
46322         //editor.on('editmodechange', this.updateToolbar, this);
46323     },
46324     
46325     
46326     relayBtnCmd : function(btn) {
46327         this.editorcore.relayCmd(btn.cmd);
46328     },
46329     // private used internally
46330     createLink : function(){
46331         Roo.log("create link?");
46332         var url = prompt(this.createLinkText, this.defaultLinkValue);
46333         if(url && url != 'http:/'+'/'){
46334             this.editorcore.relayCmd('createlink', url);
46335         }
46336     },
46337
46338     
46339     /**
46340      * Protected method that will not generally be called directly. It triggers
46341      * a toolbar update by reading the markup state of the current selection in the editor.
46342      */
46343     updateToolbar: function(){
46344
46345         if(!this.editorcore.activated){
46346             this.editor.onFirstFocus();
46347             return;
46348         }
46349
46350         var btns = this.tb.items.map, 
46351             doc = this.editorcore.doc,
46352             frameId = this.editorcore.frameId;
46353
46354         if(!this.disable.font && !Roo.isSafari){
46355             /*
46356             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46357             if(name != this.fontSelect.dom.value){
46358                 this.fontSelect.dom.value = name;
46359             }
46360             */
46361         }
46362         if(!this.disable.format){
46363             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46364             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46365             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46366             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46367         }
46368         if(!this.disable.alignments){
46369             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46370             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46371             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46372         }
46373         if(!Roo.isSafari && !this.disable.lists){
46374             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46375             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46376         }
46377         
46378         var ans = this.editorcore.getAllAncestors();
46379         if (this.formatCombo) {
46380             
46381             
46382             var store = this.formatCombo.store;
46383             this.formatCombo.setValue("");
46384             for (var i =0; i < ans.length;i++) {
46385                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46386                     // select it..
46387                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46388                     break;
46389                 }
46390             }
46391         }
46392         
46393         
46394         
46395         // hides menus... - so this cant be on a menu...
46396         Roo.menu.MenuMgr.hideAll();
46397
46398         //this.editorsyncValue();
46399     },
46400    
46401     
46402     createFontOptions : function(){
46403         var buf = [], fs = this.fontFamilies, ff, lc;
46404         
46405         
46406         
46407         for(var i = 0, len = fs.length; i< len; i++){
46408             ff = fs[i];
46409             lc = ff.toLowerCase();
46410             buf.push(
46411                 '<option value="',lc,'" style="font-family:',ff,';"',
46412                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46413                     ff,
46414                 '</option>'
46415             );
46416         }
46417         return buf.join('');
46418     },
46419     
46420     toggleSourceEdit : function(sourceEditMode){
46421         
46422         Roo.log("toolbar toogle");
46423         if(sourceEditMode === undefined){
46424             sourceEditMode = !this.sourceEditMode;
46425         }
46426         this.sourceEditMode = sourceEditMode === true;
46427         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46428         // just toggle the button?
46429         if(btn.pressed !== this.sourceEditMode){
46430             btn.toggle(this.sourceEditMode);
46431             return;
46432         }
46433         
46434         if(sourceEditMode){
46435             Roo.log("disabling buttons");
46436             this.tb.items.each(function(item){
46437                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46438                     item.disable();
46439                 }
46440             });
46441           
46442         }else{
46443             Roo.log("enabling buttons");
46444             if(this.editorcore.initialized){
46445                 this.tb.items.each(function(item){
46446                     item.enable();
46447                 });
46448             }
46449             
46450         }
46451         Roo.log("calling toggole on editor");
46452         // tell the editor that it's been pressed..
46453         this.editor.toggleSourceEdit(sourceEditMode);
46454        
46455     },
46456      /**
46457      * Object collection of toolbar tooltips for the buttons in the editor. The key
46458      * is the command id associated with that button and the value is a valid QuickTips object.
46459      * For example:
46460 <pre><code>
46461 {
46462     bold : {
46463         title: 'Bold (Ctrl+B)',
46464         text: 'Make the selected text bold.',
46465         cls: 'x-html-editor-tip'
46466     },
46467     italic : {
46468         title: 'Italic (Ctrl+I)',
46469         text: 'Make the selected text italic.',
46470         cls: 'x-html-editor-tip'
46471     },
46472     ...
46473 </code></pre>
46474     * @type Object
46475      */
46476     buttonTips : {
46477         bold : {
46478             title: 'Bold (Ctrl+B)',
46479             text: 'Make the selected text bold.',
46480             cls: 'x-html-editor-tip'
46481         },
46482         italic : {
46483             title: 'Italic (Ctrl+I)',
46484             text: 'Make the selected text italic.',
46485             cls: 'x-html-editor-tip'
46486         },
46487         underline : {
46488             title: 'Underline (Ctrl+U)',
46489             text: 'Underline the selected text.',
46490             cls: 'x-html-editor-tip'
46491         },
46492         strikethrough : {
46493             title: 'Strikethrough',
46494             text: 'Strikethrough the selected text.',
46495             cls: 'x-html-editor-tip'
46496         },
46497         increasefontsize : {
46498             title: 'Grow Text',
46499             text: 'Increase the font size.',
46500             cls: 'x-html-editor-tip'
46501         },
46502         decreasefontsize : {
46503             title: 'Shrink Text',
46504             text: 'Decrease the font size.',
46505             cls: 'x-html-editor-tip'
46506         },
46507         backcolor : {
46508             title: 'Text Highlight Color',
46509             text: 'Change the background color of the selected text.',
46510             cls: 'x-html-editor-tip'
46511         },
46512         forecolor : {
46513             title: 'Font Color',
46514             text: 'Change the color of the selected text.',
46515             cls: 'x-html-editor-tip'
46516         },
46517         justifyleft : {
46518             title: 'Align Text Left',
46519             text: 'Align text to the left.',
46520             cls: 'x-html-editor-tip'
46521         },
46522         justifycenter : {
46523             title: 'Center Text',
46524             text: 'Center text in the editor.',
46525             cls: 'x-html-editor-tip'
46526         },
46527         justifyright : {
46528             title: 'Align Text Right',
46529             text: 'Align text to the right.',
46530             cls: 'x-html-editor-tip'
46531         },
46532         insertunorderedlist : {
46533             title: 'Bullet List',
46534             text: 'Start a bulleted list.',
46535             cls: 'x-html-editor-tip'
46536         },
46537         insertorderedlist : {
46538             title: 'Numbered List',
46539             text: 'Start a numbered list.',
46540             cls: 'x-html-editor-tip'
46541         },
46542         createlink : {
46543             title: 'Hyperlink',
46544             text: 'Make the selected text a hyperlink.',
46545             cls: 'x-html-editor-tip'
46546         },
46547         sourceedit : {
46548             title: 'Source Edit',
46549             text: 'Switch to source editing mode.',
46550             cls: 'x-html-editor-tip'
46551         }
46552     },
46553     // private
46554     onDestroy : function(){
46555         if(this.rendered){
46556             
46557             this.tb.items.each(function(item){
46558                 if(item.menu){
46559                     item.menu.removeAll();
46560                     if(item.menu.el){
46561                         item.menu.el.destroy();
46562                     }
46563                 }
46564                 item.destroy();
46565             });
46566              
46567         }
46568     },
46569     onFirstFocus: function() {
46570         this.tb.items.each(function(item){
46571            item.enable();
46572         });
46573     }
46574 });
46575
46576
46577
46578
46579 // <script type="text/javascript">
46580 /*
46581  * Based on
46582  * Ext JS Library 1.1.1
46583  * Copyright(c) 2006-2007, Ext JS, LLC.
46584  *  
46585  
46586  */
46587
46588  
46589 /**
46590  * @class Roo.form.HtmlEditor.ToolbarContext
46591  * Context Toolbar
46592  * 
46593  * Usage:
46594  *
46595  new Roo.form.HtmlEditor({
46596     ....
46597     toolbars : [
46598         { xtype: 'ToolbarStandard', styles : {} }
46599         { xtype: 'ToolbarContext', disable : {} }
46600     ]
46601 })
46602
46603      
46604  * 
46605  * @config : {Object} disable List of elements to disable.. (not done yet.)
46606  * @config : {Object} styles  Map of styles available.
46607  * 
46608  */
46609
46610 Roo.form.HtmlEditor.ToolbarContext = function(config)
46611 {
46612     
46613     Roo.apply(this, config);
46614     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46615     // dont call parent... till later.
46616     this.styles = this.styles || {};
46617 }
46618
46619  
46620
46621 Roo.form.HtmlEditor.ToolbarContext.types = {
46622     'IMG' : {
46623         width : {
46624             title: "Width",
46625             width: 40
46626         },
46627         height:  {
46628             title: "Height",
46629             width: 40
46630         },
46631         align: {
46632             title: "Align",
46633             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46634             width : 80
46635             
46636         },
46637         border: {
46638             title: "Border",
46639             width: 40
46640         },
46641         alt: {
46642             title: "Alt",
46643             width: 120
46644         },
46645         src : {
46646             title: "Src",
46647             width: 220
46648         }
46649         
46650     },
46651     'A' : {
46652         name : {
46653             title: "Name",
46654             width: 50
46655         },
46656         target:  {
46657             title: "Target",
46658             width: 120
46659         },
46660         href:  {
46661             title: "Href",
46662             width: 220
46663         } // border?
46664         
46665     },
46666     'TABLE' : {
46667         rows : {
46668             title: "Rows",
46669             width: 20
46670         },
46671         cols : {
46672             title: "Cols",
46673             width: 20
46674         },
46675         width : {
46676             title: "Width",
46677             width: 40
46678         },
46679         height : {
46680             title: "Height",
46681             width: 40
46682         },
46683         border : {
46684             title: "Border",
46685             width: 20
46686         }
46687     },
46688     'TD' : {
46689         width : {
46690             title: "Width",
46691             width: 40
46692         },
46693         height : {
46694             title: "Height",
46695             width: 40
46696         },   
46697         align: {
46698             title: "Align",
46699             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46700             width: 80
46701         },
46702         valign: {
46703             title: "Valign",
46704             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46705             width: 80
46706         },
46707         colspan: {
46708             title: "Colspan",
46709             width: 20
46710             
46711         },
46712          'font-family'  : {
46713             title : "Font",
46714             style : 'fontFamily',
46715             displayField: 'display',
46716             optname : 'font-family',
46717             width: 140
46718         }
46719     },
46720     'INPUT' : {
46721         name : {
46722             title: "name",
46723             width: 120
46724         },
46725         value : {
46726             title: "Value",
46727             width: 120
46728         },
46729         width : {
46730             title: "Width",
46731             width: 40
46732         }
46733     },
46734     'LABEL' : {
46735         'for' : {
46736             title: "For",
46737             width: 120
46738         }
46739     },
46740     'TEXTAREA' : {
46741           name : {
46742             title: "name",
46743             width: 120
46744         },
46745         rows : {
46746             title: "Rows",
46747             width: 20
46748         },
46749         cols : {
46750             title: "Cols",
46751             width: 20
46752         }
46753     },
46754     'SELECT' : {
46755         name : {
46756             title: "name",
46757             width: 120
46758         },
46759         selectoptions : {
46760             title: "Options",
46761             width: 200
46762         }
46763     },
46764     
46765     // should we really allow this??
46766     // should this just be 
46767     'BODY' : {
46768         title : {
46769             title: "Title",
46770             width: 200,
46771             disabled : true
46772         }
46773     },
46774     'SPAN' : {
46775         'font-family'  : {
46776             title : "Font",
46777             style : 'fontFamily',
46778             displayField: 'display',
46779             optname : 'font-family',
46780             width: 140
46781         }
46782     },
46783     'DIV' : {
46784         'font-family'  : {
46785             title : "Font",
46786             style : 'fontFamily',
46787             displayField: 'display',
46788             optname : 'font-family',
46789             width: 140
46790         }
46791     },
46792      'P' : {
46793         'font-family'  : {
46794             title : "Font",
46795             style : 'fontFamily',
46796             displayField: 'display',
46797             optname : 'font-family',
46798             width: 140
46799         }
46800     },
46801     
46802     '*' : {
46803         // empty..
46804     }
46805
46806 };
46807
46808 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46809 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46810
46811 Roo.form.HtmlEditor.ToolbarContext.options = {
46812         'font-family'  : [ 
46813                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46814                 [ 'Courier New', 'Courier New'],
46815                 [ 'Tahoma', 'Tahoma'],
46816                 [ 'Times New Roman,serif', 'Times'],
46817                 [ 'Verdana','Verdana' ]
46818         ]
46819 };
46820
46821 // fixme - these need to be configurable..
46822  
46823
46824 //Roo.form.HtmlEditor.ToolbarContext.types
46825
46826
46827 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46828     
46829     tb: false,
46830     
46831     rendered: false,
46832     
46833     editor : false,
46834     editorcore : false,
46835     /**
46836      * @cfg {Object} disable  List of toolbar elements to disable
46837          
46838      */
46839     disable : false,
46840     /**
46841      * @cfg {Object} styles List of styles 
46842      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46843      *
46844      * These must be defined in the page, so they get rendered correctly..
46845      * .headline { }
46846      * TD.underline { }
46847      * 
46848      */
46849     styles : false,
46850     
46851     options: false,
46852     
46853     toolbars : false,
46854     
46855     init : function(editor)
46856     {
46857         this.editor = editor;
46858         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46859         var editorcore = this.editorcore;
46860         
46861         var fid = editorcore.frameId;
46862         var etb = this;
46863         function btn(id, toggle, handler){
46864             var xid = fid + '-'+ id ;
46865             return {
46866                 id : xid,
46867                 cmd : id,
46868                 cls : 'x-btn-icon x-edit-'+id,
46869                 enableToggle:toggle !== false,
46870                 scope: editorcore, // was editor...
46871                 handler:handler||editorcore.relayBtnCmd,
46872                 clickEvent:'mousedown',
46873                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46874                 tabIndex:-1
46875             };
46876         }
46877         // create a new element.
46878         var wdiv = editor.wrap.createChild({
46879                 tag: 'div'
46880             }, editor.wrap.dom.firstChild.nextSibling, true);
46881         
46882         // can we do this more than once??
46883         
46884          // stop form submits
46885       
46886  
46887         // disable everything...
46888         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46889         this.toolbars = {};
46890            
46891         for (var i in  ty) {
46892           
46893             this.toolbars[i] = this.buildToolbar(ty[i],i);
46894         }
46895         this.tb = this.toolbars.BODY;
46896         this.tb.el.show();
46897         this.buildFooter();
46898         this.footer.show();
46899         editor.on('hide', function( ) { this.footer.hide() }, this);
46900         editor.on('show', function( ) { this.footer.show() }, this);
46901         
46902          
46903         this.rendered = true;
46904         
46905         // the all the btns;
46906         editor.on('editorevent', this.updateToolbar, this);
46907         // other toolbars need to implement this..
46908         //editor.on('editmodechange', this.updateToolbar, this);
46909     },
46910     
46911     
46912     
46913     /**
46914      * Protected method that will not generally be called directly. It triggers
46915      * a toolbar update by reading the markup state of the current selection in the editor.
46916      *
46917      * Note you can force an update by calling on('editorevent', scope, false)
46918      */
46919     updateToolbar: function(editor,ev,sel){
46920
46921         //Roo.log(ev);
46922         // capture mouse up - this is handy for selecting images..
46923         // perhaps should go somewhere else...
46924         if(!this.editorcore.activated){
46925              this.editor.onFirstFocus();
46926             return;
46927         }
46928         
46929         
46930         
46931         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46932         // selectNode - might want to handle IE?
46933         if (ev &&
46934             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46935             ev.target && ev.target.tagName == 'IMG') {
46936             // they have click on an image...
46937             // let's see if we can change the selection...
46938             sel = ev.target;
46939          
46940               var nodeRange = sel.ownerDocument.createRange();
46941             try {
46942                 nodeRange.selectNode(sel);
46943             } catch (e) {
46944                 nodeRange.selectNodeContents(sel);
46945             }
46946             //nodeRange.collapse(true);
46947             var s = this.editorcore.win.getSelection();
46948             s.removeAllRanges();
46949             s.addRange(nodeRange);
46950         }  
46951         
46952       
46953         var updateFooter = sel ? false : true;
46954         
46955         
46956         var ans = this.editorcore.getAllAncestors();
46957         
46958         // pick
46959         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46960         
46961         if (!sel) { 
46962             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46963             sel = sel ? sel : this.editorcore.doc.body;
46964             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46965             
46966         }
46967         // pick a menu that exists..
46968         var tn = sel.tagName.toUpperCase();
46969         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46970         
46971         tn = sel.tagName.toUpperCase();
46972         
46973         var lastSel = this.tb.selectedNode;
46974         
46975         this.tb.selectedNode = sel;
46976         
46977         // if current menu does not match..
46978         
46979         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46980                 
46981             this.tb.el.hide();
46982             ///console.log("show: " + tn);
46983             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46984             this.tb.el.show();
46985             // update name
46986             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46987             
46988             
46989             // update attributes
46990             if (this.tb.fields) {
46991                 this.tb.fields.each(function(e) {
46992                     if (e.stylename) {
46993                         e.setValue(sel.style[e.stylename]);
46994                         return;
46995                     } 
46996                    e.setValue(sel.getAttribute(e.attrname));
46997                 });
46998             }
46999             
47000             var hasStyles = false;
47001             for(var i in this.styles) {
47002                 hasStyles = true;
47003                 break;
47004             }
47005             
47006             // update styles
47007             if (hasStyles) { 
47008                 var st = this.tb.fields.item(0);
47009                 
47010                 st.store.removeAll();
47011                
47012                 
47013                 var cn = sel.className.split(/\s+/);
47014                 
47015                 var avs = [];
47016                 if (this.styles['*']) {
47017                     
47018                     Roo.each(this.styles['*'], function(v) {
47019                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47020                     });
47021                 }
47022                 if (this.styles[tn]) { 
47023                     Roo.each(this.styles[tn], function(v) {
47024                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47025                     });
47026                 }
47027                 
47028                 st.store.loadData(avs);
47029                 st.collapse();
47030                 st.setValue(cn);
47031             }
47032             // flag our selected Node.
47033             this.tb.selectedNode = sel;
47034            
47035            
47036             Roo.menu.MenuMgr.hideAll();
47037
47038         }
47039         
47040         if (!updateFooter) {
47041             //this.footDisp.dom.innerHTML = ''; 
47042             return;
47043         }
47044         // update the footer
47045         //
47046         var html = '';
47047         
47048         this.footerEls = ans.reverse();
47049         Roo.each(this.footerEls, function(a,i) {
47050             if (!a) { return; }
47051             html += html.length ? ' &gt; '  :  '';
47052             
47053             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47054             
47055         });
47056        
47057         // 
47058         var sz = this.footDisp.up('td').getSize();
47059         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47060         this.footDisp.dom.style.marginLeft = '5px';
47061         
47062         this.footDisp.dom.style.overflow = 'hidden';
47063         
47064         this.footDisp.dom.innerHTML = html;
47065             
47066         //this.editorsyncValue();
47067     },
47068      
47069     
47070    
47071        
47072     // private
47073     onDestroy : function(){
47074         if(this.rendered){
47075             
47076             this.tb.items.each(function(item){
47077                 if(item.menu){
47078                     item.menu.removeAll();
47079                     if(item.menu.el){
47080                         item.menu.el.destroy();
47081                     }
47082                 }
47083                 item.destroy();
47084             });
47085              
47086         }
47087     },
47088     onFirstFocus: function() {
47089         // need to do this for all the toolbars..
47090         this.tb.items.each(function(item){
47091            item.enable();
47092         });
47093     },
47094     buildToolbar: function(tlist, nm)
47095     {
47096         var editor = this.editor;
47097         var editorcore = this.editorcore;
47098          // create a new element.
47099         var wdiv = editor.wrap.createChild({
47100                 tag: 'div'
47101             }, editor.wrap.dom.firstChild.nextSibling, true);
47102         
47103        
47104         var tb = new Roo.Toolbar(wdiv);
47105         // add the name..
47106         
47107         tb.add(nm+ ":&nbsp;");
47108         
47109         var styles = [];
47110         for(var i in this.styles) {
47111             styles.push(i);
47112         }
47113         
47114         // styles...
47115         if (styles && styles.length) {
47116             
47117             // this needs a multi-select checkbox...
47118             tb.addField( new Roo.form.ComboBox({
47119                 store: new Roo.data.SimpleStore({
47120                     id : 'val',
47121                     fields: ['val', 'selected'],
47122                     data : [] 
47123                 }),
47124                 name : '-roo-edit-className',
47125                 attrname : 'className',
47126                 displayField: 'val',
47127                 typeAhead: false,
47128                 mode: 'local',
47129                 editable : false,
47130                 triggerAction: 'all',
47131                 emptyText:'Select Style',
47132                 selectOnFocus:true,
47133                 width: 130,
47134                 listeners : {
47135                     'select': function(c, r, i) {
47136                         // initial support only for on class per el..
47137                         tb.selectedNode.className =  r ? r.get('val') : '';
47138                         editorcore.syncValue();
47139                     }
47140                 }
47141     
47142             }));
47143         }
47144         
47145         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47146         var tbops = tbc.options;
47147         
47148         for (var i in tlist) {
47149             
47150             var item = tlist[i];
47151             tb.add(item.title + ":&nbsp;");
47152             
47153             
47154             //optname == used so you can configure the options available..
47155             var opts = item.opts ? item.opts : false;
47156             if (item.optname) {
47157                 opts = tbops[item.optname];
47158            
47159             }
47160             
47161             if (opts) {
47162                 // opts == pulldown..
47163                 tb.addField( new Roo.form.ComboBox({
47164                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47165                         id : 'val',
47166                         fields: ['val', 'display'],
47167                         data : opts  
47168                     }),
47169                     name : '-roo-edit-' + i,
47170                     attrname : i,
47171                     stylename : item.style ? item.style : false,
47172                     displayField: item.displayField ? item.displayField : 'val',
47173                     valueField :  'val',
47174                     typeAhead: false,
47175                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47176                     editable : false,
47177                     triggerAction: 'all',
47178                     emptyText:'Select',
47179                     selectOnFocus:true,
47180                     width: item.width ? item.width  : 130,
47181                     listeners : {
47182                         'select': function(c, r, i) {
47183                             if (c.stylename) {
47184                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47185                                 return;
47186                             }
47187                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47188                         }
47189                     }
47190
47191                 }));
47192                 continue;
47193                     
47194                  
47195                 
47196                 tb.addField( new Roo.form.TextField({
47197                     name: i,
47198                     width: 100,
47199                     //allowBlank:false,
47200                     value: ''
47201                 }));
47202                 continue;
47203             }
47204             tb.addField( new Roo.form.TextField({
47205                 name: '-roo-edit-' + i,
47206                 attrname : i,
47207                 
47208                 width: item.width,
47209                 //allowBlank:true,
47210                 value: '',
47211                 listeners: {
47212                     'change' : function(f, nv, ov) {
47213                         tb.selectedNode.setAttribute(f.attrname, nv);
47214                         editorcore.syncValue();
47215                     }
47216                 }
47217             }));
47218              
47219         }
47220         
47221         var _this = this;
47222         
47223         if(nm == 'BODY'){
47224             tb.addSeparator();
47225         
47226             tb.addButton( {
47227                 text: 'Stylesheets',
47228
47229                 listeners : {
47230                     click : function ()
47231                     {
47232                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47233                     }
47234                 }
47235             });
47236         }
47237         
47238         tb.addFill();
47239         tb.addButton( {
47240             text: 'Remove Tag',
47241     
47242             listeners : {
47243                 click : function ()
47244                 {
47245                     // remove
47246                     // undo does not work.
47247                      
47248                     var sn = tb.selectedNode;
47249                     
47250                     var pn = sn.parentNode;
47251                     
47252                     var stn =  sn.childNodes[0];
47253                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47254                     while (sn.childNodes.length) {
47255                         var node = sn.childNodes[0];
47256                         sn.removeChild(node);
47257                         //Roo.log(node);
47258                         pn.insertBefore(node, sn);
47259                         
47260                     }
47261                     pn.removeChild(sn);
47262                     var range = editorcore.createRange();
47263         
47264                     range.setStart(stn,0);
47265                     range.setEnd(en,0); //????
47266                     //range.selectNode(sel);
47267                     
47268                     
47269                     var selection = editorcore.getSelection();
47270                     selection.removeAllRanges();
47271                     selection.addRange(range);
47272                     
47273                     
47274                     
47275                     //_this.updateToolbar(null, null, pn);
47276                     _this.updateToolbar(null, null, null);
47277                     _this.footDisp.dom.innerHTML = ''; 
47278                 }
47279             }
47280             
47281                     
47282                 
47283             
47284         });
47285         
47286         
47287         tb.el.on('click', function(e){
47288             e.preventDefault(); // what does this do?
47289         });
47290         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47291         tb.el.hide();
47292         tb.name = nm;
47293         // dont need to disable them... as they will get hidden
47294         return tb;
47295          
47296         
47297     },
47298     buildFooter : function()
47299     {
47300         
47301         var fel = this.editor.wrap.createChild();
47302         this.footer = new Roo.Toolbar(fel);
47303         // toolbar has scrolly on left / right?
47304         var footDisp= new Roo.Toolbar.Fill();
47305         var _t = this;
47306         this.footer.add(
47307             {
47308                 text : '&lt;',
47309                 xtype: 'Button',
47310                 handler : function() {
47311                     _t.footDisp.scrollTo('left',0,true)
47312                 }
47313             }
47314         );
47315         this.footer.add( footDisp );
47316         this.footer.add( 
47317             {
47318                 text : '&gt;',
47319                 xtype: 'Button',
47320                 handler : function() {
47321                     // no animation..
47322                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47323                 }
47324             }
47325         );
47326         var fel = Roo.get(footDisp.el);
47327         fel.addClass('x-editor-context');
47328         this.footDispWrap = fel; 
47329         this.footDispWrap.overflow  = 'hidden';
47330         
47331         this.footDisp = fel.createChild();
47332         this.footDispWrap.on('click', this.onContextClick, this)
47333         
47334         
47335     },
47336     onContextClick : function (ev,dom)
47337     {
47338         ev.preventDefault();
47339         var  cn = dom.className;
47340         //Roo.log(cn);
47341         if (!cn.match(/x-ed-loc-/)) {
47342             return;
47343         }
47344         var n = cn.split('-').pop();
47345         var ans = this.footerEls;
47346         var sel = ans[n];
47347         
47348          // pick
47349         var range = this.editorcore.createRange();
47350         
47351         range.selectNodeContents(sel);
47352         //range.selectNode(sel);
47353         
47354         
47355         var selection = this.editorcore.getSelection();
47356         selection.removeAllRanges();
47357         selection.addRange(range);
47358         
47359         
47360         
47361         this.updateToolbar(null, null, sel);
47362         
47363         
47364     }
47365     
47366     
47367     
47368     
47369     
47370 });
47371
47372
47373
47374
47375
47376 /*
47377  * Based on:
47378  * Ext JS Library 1.1.1
47379  * Copyright(c) 2006-2007, Ext JS, LLC.
47380  *
47381  * Originally Released Under LGPL - original licence link has changed is not relivant.
47382  *
47383  * Fork - LGPL
47384  * <script type="text/javascript">
47385  */
47386  
47387 /**
47388  * @class Roo.form.BasicForm
47389  * @extends Roo.util.Observable
47390  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47391  * @constructor
47392  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47393  * @param {Object} config Configuration options
47394  */
47395 Roo.form.BasicForm = function(el, config){
47396     this.allItems = [];
47397     this.childForms = [];
47398     Roo.apply(this, config);
47399     /*
47400      * The Roo.form.Field items in this form.
47401      * @type MixedCollection
47402      */
47403      
47404      
47405     this.items = new Roo.util.MixedCollection(false, function(o){
47406         return o.id || (o.id = Roo.id());
47407     });
47408     this.addEvents({
47409         /**
47410          * @event beforeaction
47411          * Fires before any action is performed. Return false to cancel the action.
47412          * @param {Form} this
47413          * @param {Action} action The action to be performed
47414          */
47415         beforeaction: true,
47416         /**
47417          * @event actionfailed
47418          * Fires when an action fails.
47419          * @param {Form} this
47420          * @param {Action} action The action that failed
47421          */
47422         actionfailed : true,
47423         /**
47424          * @event actioncomplete
47425          * Fires when an action is completed.
47426          * @param {Form} this
47427          * @param {Action} action The action that completed
47428          */
47429         actioncomplete : true
47430     });
47431     if(el){
47432         this.initEl(el);
47433     }
47434     Roo.form.BasicForm.superclass.constructor.call(this);
47435     
47436     Roo.form.BasicForm.popover.apply();
47437 };
47438
47439 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47440     /**
47441      * @cfg {String} method
47442      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47443      */
47444     /**
47445      * @cfg {DataReader} reader
47446      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47447      * This is optional as there is built-in support for processing JSON.
47448      */
47449     /**
47450      * @cfg {DataReader} errorReader
47451      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47452      * This is completely optional as there is built-in support for processing JSON.
47453      */
47454     /**
47455      * @cfg {String} url
47456      * The URL to use for form actions if one isn't supplied in the action options.
47457      */
47458     /**
47459      * @cfg {Boolean} fileUpload
47460      * Set to true if this form is a file upload.
47461      */
47462      
47463     /**
47464      * @cfg {Object} baseParams
47465      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47466      */
47467      /**
47468      
47469     /**
47470      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47471      */
47472     timeout: 30,
47473
47474     // private
47475     activeAction : null,
47476
47477     /**
47478      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47479      * or setValues() data instead of when the form was first created.
47480      */
47481     trackResetOnLoad : false,
47482     
47483     
47484     /**
47485      * childForms - used for multi-tab forms
47486      * @type {Array}
47487      */
47488     childForms : false,
47489     
47490     /**
47491      * allItems - full list of fields.
47492      * @type {Array}
47493      */
47494     allItems : false,
47495     
47496     /**
47497      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47498      * element by passing it or its id or mask the form itself by passing in true.
47499      * @type Mixed
47500      */
47501     waitMsgTarget : false,
47502     
47503     /**
47504      * @type Boolean
47505      */
47506     disableMask : false,
47507     
47508     /**
47509      * @cfg {Boolean} errorMask (true|false) default false
47510      */
47511     errorMask : false,
47512     
47513     /**
47514      * @cfg {Number} maskOffset Default 100
47515      */
47516     maskOffset : 100,
47517
47518     // private
47519     initEl : function(el){
47520         this.el = Roo.get(el);
47521         this.id = this.el.id || Roo.id();
47522         this.el.on('submit', this.onSubmit, this);
47523         this.el.addClass('x-form');
47524     },
47525
47526     // private
47527     onSubmit : function(e){
47528         e.stopEvent();
47529     },
47530
47531     /**
47532      * Returns true if client-side validation on the form is successful.
47533      * @return Boolean
47534      */
47535     isValid : function(){
47536         var valid = true;
47537         var target = false;
47538         this.items.each(function(f){
47539             if(f.validate()){
47540                 return;
47541             }
47542             
47543             valid = false;
47544                 
47545             if(!target && f.el.isVisible(true)){
47546                 target = f;
47547             }
47548         });
47549         
47550         if(this.errorMask && !valid){
47551             Roo.form.BasicForm.popover.mask(this, target);
47552         }
47553         
47554         return valid;
47555     },
47556
47557     /**
47558      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47559      * @return Boolean
47560      */
47561     isDirty : function(){
47562         var dirty = false;
47563         this.items.each(function(f){
47564            if(f.isDirty()){
47565                dirty = true;
47566                return false;
47567            }
47568         });
47569         return dirty;
47570     },
47571     
47572     /**
47573      * Returns true if any fields in this form have changed since their original load. (New version)
47574      * @return Boolean
47575      */
47576     
47577     hasChanged : function()
47578     {
47579         var dirty = false;
47580         this.items.each(function(f){
47581            if(f.hasChanged()){
47582                dirty = true;
47583                return false;
47584            }
47585         });
47586         return dirty;
47587         
47588     },
47589     /**
47590      * Resets all hasChanged to 'false' -
47591      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47592      * So hasChanged storage is only to be used for this purpose
47593      * @return Boolean
47594      */
47595     resetHasChanged : function()
47596     {
47597         this.items.each(function(f){
47598            f.resetHasChanged();
47599         });
47600         
47601     },
47602     
47603     
47604     /**
47605      * Performs a predefined action (submit or load) or custom actions you define on this form.
47606      * @param {String} actionName The name of the action type
47607      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47608      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47609      * accept other config options):
47610      * <pre>
47611 Property          Type             Description
47612 ----------------  ---------------  ----------------------------------------------------------------------------------
47613 url               String           The url for the action (defaults to the form's url)
47614 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47615 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47616 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47617                                    validate the form on the client (defaults to false)
47618      * </pre>
47619      * @return {BasicForm} this
47620      */
47621     doAction : function(action, options){
47622         if(typeof action == 'string'){
47623             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47624         }
47625         if(this.fireEvent('beforeaction', this, action) !== false){
47626             this.beforeAction(action);
47627             action.run.defer(100, action);
47628         }
47629         return this;
47630     },
47631
47632     /**
47633      * Shortcut to do a submit action.
47634      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47635      * @return {BasicForm} this
47636      */
47637     submit : function(options){
47638         this.doAction('submit', options);
47639         return this;
47640     },
47641
47642     /**
47643      * Shortcut to do a load action.
47644      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47645      * @return {BasicForm} this
47646      */
47647     load : function(options){
47648         this.doAction('load', options);
47649         return this;
47650     },
47651
47652     /**
47653      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47654      * @param {Record} record The record to edit
47655      * @return {BasicForm} this
47656      */
47657     updateRecord : function(record){
47658         record.beginEdit();
47659         var fs = record.fields;
47660         fs.each(function(f){
47661             var field = this.findField(f.name);
47662             if(field){
47663                 record.set(f.name, field.getValue());
47664             }
47665         }, this);
47666         record.endEdit();
47667         return this;
47668     },
47669
47670     /**
47671      * Loads an Roo.data.Record into this form.
47672      * @param {Record} record The record to load
47673      * @return {BasicForm} this
47674      */
47675     loadRecord : function(record){
47676         this.setValues(record.data);
47677         return this;
47678     },
47679
47680     // private
47681     beforeAction : function(action){
47682         var o = action.options;
47683         
47684         if(!this.disableMask) {
47685             if(this.waitMsgTarget === true){
47686                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47687             }else if(this.waitMsgTarget){
47688                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47689                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47690             }else {
47691                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47692             }
47693         }
47694         
47695          
47696     },
47697
47698     // private
47699     afterAction : function(action, success){
47700         this.activeAction = null;
47701         var o = action.options;
47702         
47703         if(!this.disableMask) {
47704             if(this.waitMsgTarget === true){
47705                 this.el.unmask();
47706             }else if(this.waitMsgTarget){
47707                 this.waitMsgTarget.unmask();
47708             }else{
47709                 Roo.MessageBox.updateProgress(1);
47710                 Roo.MessageBox.hide();
47711             }
47712         }
47713         
47714         if(success){
47715             if(o.reset){
47716                 this.reset();
47717             }
47718             Roo.callback(o.success, o.scope, [this, action]);
47719             this.fireEvent('actioncomplete', this, action);
47720             
47721         }else{
47722             
47723             // failure condition..
47724             // we have a scenario where updates need confirming.
47725             // eg. if a locking scenario exists..
47726             // we look for { errors : { needs_confirm : true }} in the response.
47727             if (
47728                 (typeof(action.result) != 'undefined')  &&
47729                 (typeof(action.result.errors) != 'undefined')  &&
47730                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47731            ){
47732                 var _t = this;
47733                 Roo.MessageBox.confirm(
47734                     "Change requires confirmation",
47735                     action.result.errorMsg,
47736                     function(r) {
47737                         if (r != 'yes') {
47738                             return;
47739                         }
47740                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47741                     }
47742                     
47743                 );
47744                 
47745                 
47746                 
47747                 return;
47748             }
47749             
47750             Roo.callback(o.failure, o.scope, [this, action]);
47751             // show an error message if no failed handler is set..
47752             if (!this.hasListener('actionfailed')) {
47753                 Roo.MessageBox.alert("Error",
47754                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47755                         action.result.errorMsg :
47756                         "Saving Failed, please check your entries or try again"
47757                 );
47758             }
47759             
47760             this.fireEvent('actionfailed', this, action);
47761         }
47762         
47763     },
47764
47765     /**
47766      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47767      * @param {String} id The value to search for
47768      * @return Field
47769      */
47770     findField : function(id){
47771         var field = this.items.get(id);
47772         if(!field){
47773             this.items.each(function(f){
47774                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47775                     field = f;
47776                     return false;
47777                 }
47778             });
47779         }
47780         return field || null;
47781     },
47782
47783     /**
47784      * Add a secondary form to this one, 
47785      * Used to provide tabbed forms. One form is primary, with hidden values 
47786      * which mirror the elements from the other forms.
47787      * 
47788      * @param {Roo.form.Form} form to add.
47789      * 
47790      */
47791     addForm : function(form)
47792     {
47793        
47794         if (this.childForms.indexOf(form) > -1) {
47795             // already added..
47796             return;
47797         }
47798         this.childForms.push(form);
47799         var n = '';
47800         Roo.each(form.allItems, function (fe) {
47801             
47802             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47803             if (this.findField(n)) { // already added..
47804                 return;
47805             }
47806             var add = new Roo.form.Hidden({
47807                 name : n
47808             });
47809             add.render(this.el);
47810             
47811             this.add( add );
47812         }, this);
47813         
47814     },
47815     /**
47816      * Mark fields in this form invalid in bulk.
47817      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47818      * @return {BasicForm} this
47819      */
47820     markInvalid : function(errors){
47821         if(errors instanceof Array){
47822             for(var i = 0, len = errors.length; i < len; i++){
47823                 var fieldError = errors[i];
47824                 var f = this.findField(fieldError.id);
47825                 if(f){
47826                     f.markInvalid(fieldError.msg);
47827                 }
47828             }
47829         }else{
47830             var field, id;
47831             for(id in errors){
47832                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47833                     field.markInvalid(errors[id]);
47834                 }
47835             }
47836         }
47837         Roo.each(this.childForms || [], function (f) {
47838             f.markInvalid(errors);
47839         });
47840         
47841         return this;
47842     },
47843
47844     /**
47845      * Set values for fields in this form in bulk.
47846      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47847      * @return {BasicForm} this
47848      */
47849     setValues : function(values){
47850         if(values instanceof Array){ // array of objects
47851             for(var i = 0, len = values.length; i < len; i++){
47852                 var v = values[i];
47853                 var f = this.findField(v.id);
47854                 if(f){
47855                     f.setValue(v.value);
47856                     if(this.trackResetOnLoad){
47857                         f.originalValue = f.getValue();
47858                     }
47859                 }
47860             }
47861         }else{ // object hash
47862             var field, id;
47863             for(id in values){
47864                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47865                     
47866                     if (field.setFromData && 
47867                         field.valueField && 
47868                         field.displayField &&
47869                         // combos' with local stores can 
47870                         // be queried via setValue()
47871                         // to set their value..
47872                         (field.store && !field.store.isLocal)
47873                         ) {
47874                         // it's a combo
47875                         var sd = { };
47876                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47877                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47878                         field.setFromData(sd);
47879                         
47880                     } else {
47881                         field.setValue(values[id]);
47882                     }
47883                     
47884                     
47885                     if(this.trackResetOnLoad){
47886                         field.originalValue = field.getValue();
47887                     }
47888                 }
47889             }
47890         }
47891         this.resetHasChanged();
47892         
47893         
47894         Roo.each(this.childForms || [], function (f) {
47895             f.setValues(values);
47896             f.resetHasChanged();
47897         });
47898                 
47899         return this;
47900     },
47901  
47902     /**
47903      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47904      * they are returned as an array.
47905      * @param {Boolean} asString
47906      * @return {Object}
47907      */
47908     getValues : function(asString){
47909         if (this.childForms) {
47910             // copy values from the child forms
47911             Roo.each(this.childForms, function (f) {
47912                 this.setValues(f.getValues());
47913             }, this);
47914         }
47915         
47916         // use formdata
47917         if (typeof(FormData) != 'undefined' && asString !== true) {
47918             // this relies on a 'recent' version of chrome apparently...
47919             try {
47920                 var fd = (new FormData(this.el.dom)).entries();
47921                 var ret = {};
47922                 var ent = fd.next();
47923                 while (!ent.done) {
47924                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47925                     ent = fd.next();
47926                 };
47927                 return ret;
47928             } catch(e) {
47929                 
47930             }
47931             
47932         }
47933         
47934         
47935         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47936         if(asString === true){
47937             return fs;
47938         }
47939         return Roo.urlDecode(fs);
47940     },
47941     
47942     /**
47943      * Returns the fields in this form as an object with key/value pairs. 
47944      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47945      * @return {Object}
47946      */
47947     getFieldValues : function(with_hidden)
47948     {
47949         if (this.childForms) {
47950             // copy values from the child forms
47951             // should this call getFieldValues - probably not as we do not currently copy
47952             // hidden fields when we generate..
47953             Roo.each(this.childForms, function (f) {
47954                 this.setValues(f.getValues());
47955             }, this);
47956         }
47957         
47958         var ret = {};
47959         this.items.each(function(f){
47960             if (!f.getName()) {
47961                 return;
47962             }
47963             var v = f.getValue();
47964             if (f.inputType =='radio') {
47965                 if (typeof(ret[f.getName()]) == 'undefined') {
47966                     ret[f.getName()] = ''; // empty..
47967                 }
47968                 
47969                 if (!f.el.dom.checked) {
47970                     return;
47971                     
47972                 }
47973                 v = f.el.dom.value;
47974                 
47975             }
47976             
47977             // not sure if this supported any more..
47978             if ((typeof(v) == 'object') && f.getRawValue) {
47979                 v = f.getRawValue() ; // dates..
47980             }
47981             // combo boxes where name != hiddenName...
47982             if (f.name != f.getName()) {
47983                 ret[f.name] = f.getRawValue();
47984             }
47985             ret[f.getName()] = v;
47986         });
47987         
47988         return ret;
47989     },
47990
47991     /**
47992      * Clears all invalid messages in this form.
47993      * @return {BasicForm} this
47994      */
47995     clearInvalid : function(){
47996         this.items.each(function(f){
47997            f.clearInvalid();
47998         });
47999         
48000         Roo.each(this.childForms || [], function (f) {
48001             f.clearInvalid();
48002         });
48003         
48004         
48005         return this;
48006     },
48007
48008     /**
48009      * Resets this form.
48010      * @return {BasicForm} this
48011      */
48012     reset : function(){
48013         this.items.each(function(f){
48014             f.reset();
48015         });
48016         
48017         Roo.each(this.childForms || [], function (f) {
48018             f.reset();
48019         });
48020         this.resetHasChanged();
48021         
48022         return this;
48023     },
48024
48025     /**
48026      * Add Roo.form components to this form.
48027      * @param {Field} field1
48028      * @param {Field} field2 (optional)
48029      * @param {Field} etc (optional)
48030      * @return {BasicForm} this
48031      */
48032     add : function(){
48033         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48034         return this;
48035     },
48036
48037
48038     /**
48039      * Removes a field from the items collection (does NOT remove its markup).
48040      * @param {Field} field
48041      * @return {BasicForm} this
48042      */
48043     remove : function(field){
48044         this.items.remove(field);
48045         return this;
48046     },
48047
48048     /**
48049      * Looks at the fields in this form, checks them for an id attribute,
48050      * and calls applyTo on the existing dom element with that id.
48051      * @return {BasicForm} this
48052      */
48053     render : function(){
48054         this.items.each(function(f){
48055             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48056                 f.applyTo(f.id);
48057             }
48058         });
48059         return this;
48060     },
48061
48062     /**
48063      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48064      * @param {Object} values
48065      * @return {BasicForm} this
48066      */
48067     applyToFields : function(o){
48068         this.items.each(function(f){
48069            Roo.apply(f, o);
48070         });
48071         return this;
48072     },
48073
48074     /**
48075      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48076      * @param {Object} values
48077      * @return {BasicForm} this
48078      */
48079     applyIfToFields : function(o){
48080         this.items.each(function(f){
48081            Roo.applyIf(f, o);
48082         });
48083         return this;
48084     }
48085 });
48086
48087 // back compat
48088 Roo.BasicForm = Roo.form.BasicForm;
48089
48090 Roo.apply(Roo.form.BasicForm, {
48091     
48092     popover : {
48093         
48094         padding : 5,
48095         
48096         isApplied : false,
48097         
48098         isMasked : false,
48099         
48100         form : false,
48101         
48102         target : false,
48103         
48104         intervalID : false,
48105         
48106         maskEl : false,
48107         
48108         apply : function()
48109         {
48110             if(this.isApplied){
48111                 return;
48112             }
48113             
48114             this.maskEl = {
48115                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48116                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48117                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48118                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48119             };
48120             
48121             this.maskEl.top.enableDisplayMode("block");
48122             this.maskEl.left.enableDisplayMode("block");
48123             this.maskEl.bottom.enableDisplayMode("block");
48124             this.maskEl.right.enableDisplayMode("block");
48125             
48126             Roo.get(document.body).on('click', function(){
48127                 this.unmask();
48128             }, this);
48129             
48130             Roo.get(document.body).on('touchstart', function(){
48131                 this.unmask();
48132             }, this);
48133             
48134             this.isApplied = true
48135         },
48136         
48137         mask : function(form, target)
48138         {
48139             this.form = form;
48140             
48141             this.target = target;
48142             
48143             if(!this.form.errorMask || !target.el){
48144                 return;
48145             }
48146             
48147             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48148             
48149             var ot = this.target.el.calcOffsetsTo(scrollable);
48150             
48151             var scrollTo = ot[1] - this.form.maskOffset;
48152             
48153             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48154             
48155             scrollable.scrollTo('top', scrollTo);
48156             
48157             var el = this.target.wrap || this.target.el;
48158             
48159             var box = el.getBox();
48160             
48161             this.maskEl.top.setStyle('position', 'absolute');
48162             this.maskEl.top.setStyle('z-index', 10000);
48163             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48164             this.maskEl.top.setLeft(0);
48165             this.maskEl.top.setTop(0);
48166             this.maskEl.top.show();
48167             
48168             this.maskEl.left.setStyle('position', 'absolute');
48169             this.maskEl.left.setStyle('z-index', 10000);
48170             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48171             this.maskEl.left.setLeft(0);
48172             this.maskEl.left.setTop(box.y - this.padding);
48173             this.maskEl.left.show();
48174
48175             this.maskEl.bottom.setStyle('position', 'absolute');
48176             this.maskEl.bottom.setStyle('z-index', 10000);
48177             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48178             this.maskEl.bottom.setLeft(0);
48179             this.maskEl.bottom.setTop(box.bottom + this.padding);
48180             this.maskEl.bottom.show();
48181
48182             this.maskEl.right.setStyle('position', 'absolute');
48183             this.maskEl.right.setStyle('z-index', 10000);
48184             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48185             this.maskEl.right.setLeft(box.right + this.padding);
48186             this.maskEl.right.setTop(box.y - this.padding);
48187             this.maskEl.right.show();
48188
48189             this.intervalID = window.setInterval(function() {
48190                 Roo.form.BasicForm.popover.unmask();
48191             }, 10000);
48192
48193             window.onwheel = function(){ return false;};
48194             
48195             (function(){ this.isMasked = true; }).defer(500, this);
48196             
48197         },
48198         
48199         unmask : function()
48200         {
48201             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48202                 return;
48203             }
48204             
48205             this.maskEl.top.setStyle('position', 'absolute');
48206             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48207             this.maskEl.top.hide();
48208
48209             this.maskEl.left.setStyle('position', 'absolute');
48210             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48211             this.maskEl.left.hide();
48212
48213             this.maskEl.bottom.setStyle('position', 'absolute');
48214             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48215             this.maskEl.bottom.hide();
48216
48217             this.maskEl.right.setStyle('position', 'absolute');
48218             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48219             this.maskEl.right.hide();
48220             
48221             window.onwheel = function(){ return true;};
48222             
48223             if(this.intervalID){
48224                 window.clearInterval(this.intervalID);
48225                 this.intervalID = false;
48226             }
48227             
48228             this.isMasked = false;
48229             
48230         }
48231         
48232     }
48233     
48234 });/*
48235  * Based on:
48236  * Ext JS Library 1.1.1
48237  * Copyright(c) 2006-2007, Ext JS, LLC.
48238  *
48239  * Originally Released Under LGPL - original licence link has changed is not relivant.
48240  *
48241  * Fork - LGPL
48242  * <script type="text/javascript">
48243  */
48244
48245 /**
48246  * @class Roo.form.Form
48247  * @extends Roo.form.BasicForm
48248  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48249  * @constructor
48250  * @param {Object} config Configuration options
48251  */
48252 Roo.form.Form = function(config){
48253     var xitems =  [];
48254     if (config.items) {
48255         xitems = config.items;
48256         delete config.items;
48257     }
48258    
48259     
48260     Roo.form.Form.superclass.constructor.call(this, null, config);
48261     this.url = this.url || this.action;
48262     if(!this.root){
48263         this.root = new Roo.form.Layout(Roo.applyIf({
48264             id: Roo.id()
48265         }, config));
48266     }
48267     this.active = this.root;
48268     /**
48269      * Array of all the buttons that have been added to this form via {@link addButton}
48270      * @type Array
48271      */
48272     this.buttons = [];
48273     this.allItems = [];
48274     this.addEvents({
48275         /**
48276          * @event clientvalidation
48277          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48278          * @param {Form} this
48279          * @param {Boolean} valid true if the form has passed client-side validation
48280          */
48281         clientvalidation: true,
48282         /**
48283          * @event rendered
48284          * Fires when the form is rendered
48285          * @param {Roo.form.Form} form
48286          */
48287         rendered : true
48288     });
48289     
48290     if (this.progressUrl) {
48291             // push a hidden field onto the list of fields..
48292             this.addxtype( {
48293                     xns: Roo.form, 
48294                     xtype : 'Hidden', 
48295                     name : 'UPLOAD_IDENTIFIER' 
48296             });
48297         }
48298         
48299     
48300     Roo.each(xitems, this.addxtype, this);
48301     
48302 };
48303
48304 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48305     /**
48306      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48307      */
48308     /**
48309      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48310      */
48311     /**
48312      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48313      */
48314     buttonAlign:'center',
48315
48316     /**
48317      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48318      */
48319     minButtonWidth:75,
48320
48321     /**
48322      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48323      * This property cascades to child containers if not set.
48324      */
48325     labelAlign:'left',
48326
48327     /**
48328      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48329      * fires a looping event with that state. This is required to bind buttons to the valid
48330      * state using the config value formBind:true on the button.
48331      */
48332     monitorValid : false,
48333
48334     /**
48335      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48336      */
48337     monitorPoll : 200,
48338     
48339     /**
48340      * @cfg {String} progressUrl - Url to return progress data 
48341      */
48342     
48343     progressUrl : false,
48344     /**
48345      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48346      * sending a formdata with extra parameters - eg uploaded elements.
48347      */
48348     
48349     formData : false,
48350     
48351     /**
48352      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48353      * fields are added and the column is closed. If no fields are passed the column remains open
48354      * until end() is called.
48355      * @param {Object} config The config to pass to the column
48356      * @param {Field} field1 (optional)
48357      * @param {Field} field2 (optional)
48358      * @param {Field} etc (optional)
48359      * @return Column The column container object
48360      */
48361     column : function(c){
48362         var col = new Roo.form.Column(c);
48363         this.start(col);
48364         if(arguments.length > 1){ // duplicate code required because of Opera
48365             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48366             this.end();
48367         }
48368         return col;
48369     },
48370
48371     /**
48372      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48373      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48374      * until end() is called.
48375      * @param {Object} config The config to pass to the fieldset
48376      * @param {Field} field1 (optional)
48377      * @param {Field} field2 (optional)
48378      * @param {Field} etc (optional)
48379      * @return FieldSet The fieldset container object
48380      */
48381     fieldset : function(c){
48382         var fs = new Roo.form.FieldSet(c);
48383         this.start(fs);
48384         if(arguments.length > 1){ // duplicate code required because of Opera
48385             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48386             this.end();
48387         }
48388         return fs;
48389     },
48390
48391     /**
48392      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48393      * fields are added and the container is closed. If no fields are passed the container remains open
48394      * until end() is called.
48395      * @param {Object} config The config to pass to the Layout
48396      * @param {Field} field1 (optional)
48397      * @param {Field} field2 (optional)
48398      * @param {Field} etc (optional)
48399      * @return Layout The container object
48400      */
48401     container : function(c){
48402         var l = new Roo.form.Layout(c);
48403         this.start(l);
48404         if(arguments.length > 1){ // duplicate code required because of Opera
48405             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48406             this.end();
48407         }
48408         return l;
48409     },
48410
48411     /**
48412      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48413      * @param {Object} container A Roo.form.Layout or subclass of Layout
48414      * @return {Form} this
48415      */
48416     start : function(c){
48417         // cascade label info
48418         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48419         this.active.stack.push(c);
48420         c.ownerCt = this.active;
48421         this.active = c;
48422         return this;
48423     },
48424
48425     /**
48426      * Closes the current open container
48427      * @return {Form} this
48428      */
48429     end : function(){
48430         if(this.active == this.root){
48431             return this;
48432         }
48433         this.active = this.active.ownerCt;
48434         return this;
48435     },
48436
48437     /**
48438      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48439      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48440      * as the label of the field.
48441      * @param {Field} field1
48442      * @param {Field} field2 (optional)
48443      * @param {Field} etc. (optional)
48444      * @return {Form} this
48445      */
48446     add : function(){
48447         this.active.stack.push.apply(this.active.stack, arguments);
48448         this.allItems.push.apply(this.allItems,arguments);
48449         var r = [];
48450         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48451             if(a[i].isFormField){
48452                 r.push(a[i]);
48453             }
48454         }
48455         if(r.length > 0){
48456             Roo.form.Form.superclass.add.apply(this, r);
48457         }
48458         return this;
48459     },
48460     
48461
48462     
48463     
48464     
48465      /**
48466      * Find any element that has been added to a form, using it's ID or name
48467      * This can include framesets, columns etc. along with regular fields..
48468      * @param {String} id - id or name to find.
48469      
48470      * @return {Element} e - or false if nothing found.
48471      */
48472     findbyId : function(id)
48473     {
48474         var ret = false;
48475         if (!id) {
48476             return ret;
48477         }
48478         Roo.each(this.allItems, function(f){
48479             if (f.id == id || f.name == id ){
48480                 ret = f;
48481                 return false;
48482             }
48483         });
48484         return ret;
48485     },
48486
48487     
48488     
48489     /**
48490      * Render this form into the passed container. This should only be called once!
48491      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48492      * @return {Form} this
48493      */
48494     render : function(ct)
48495     {
48496         
48497         
48498         
48499         ct = Roo.get(ct);
48500         var o = this.autoCreate || {
48501             tag: 'form',
48502             method : this.method || 'POST',
48503             id : this.id || Roo.id()
48504         };
48505         this.initEl(ct.createChild(o));
48506
48507         this.root.render(this.el);
48508         
48509        
48510              
48511         this.items.each(function(f){
48512             f.render('x-form-el-'+f.id);
48513         });
48514
48515         if(this.buttons.length > 0){
48516             // tables are required to maintain order and for correct IE layout
48517             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48518                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48519                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48520             }}, null, true);
48521             var tr = tb.getElementsByTagName('tr')[0];
48522             for(var i = 0, len = this.buttons.length; i < len; i++) {
48523                 var b = this.buttons[i];
48524                 var td = document.createElement('td');
48525                 td.className = 'x-form-btn-td';
48526                 b.render(tr.appendChild(td));
48527             }
48528         }
48529         if(this.monitorValid){ // initialize after render
48530             this.startMonitoring();
48531         }
48532         this.fireEvent('rendered', this);
48533         return this;
48534     },
48535
48536     /**
48537      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48538      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48539      * object or a valid Roo.DomHelper element config
48540      * @param {Function} handler The function called when the button is clicked
48541      * @param {Object} scope (optional) The scope of the handler function
48542      * @return {Roo.Button}
48543      */
48544     addButton : function(config, handler, scope){
48545         var bc = {
48546             handler: handler,
48547             scope: scope,
48548             minWidth: this.minButtonWidth,
48549             hideParent:true
48550         };
48551         if(typeof config == "string"){
48552             bc.text = config;
48553         }else{
48554             Roo.apply(bc, config);
48555         }
48556         var btn = new Roo.Button(null, bc);
48557         this.buttons.push(btn);
48558         return btn;
48559     },
48560
48561      /**
48562      * Adds a series of form elements (using the xtype property as the factory method.
48563      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48564      * @param {Object} config 
48565      */
48566     
48567     addxtype : function()
48568     {
48569         var ar = Array.prototype.slice.call(arguments, 0);
48570         var ret = false;
48571         for(var i = 0; i < ar.length; i++) {
48572             if (!ar[i]) {
48573                 continue; // skip -- if this happends something invalid got sent, we 
48574                 // should ignore it, as basically that interface element will not show up
48575                 // and that should be pretty obvious!!
48576             }
48577             
48578             if (Roo.form[ar[i].xtype]) {
48579                 ar[i].form = this;
48580                 var fe = Roo.factory(ar[i], Roo.form);
48581                 if (!ret) {
48582                     ret = fe;
48583                 }
48584                 fe.form = this;
48585                 if (fe.store) {
48586                     fe.store.form = this;
48587                 }
48588                 if (fe.isLayout) {  
48589                          
48590                     this.start(fe);
48591                     this.allItems.push(fe);
48592                     if (fe.items && fe.addxtype) {
48593                         fe.addxtype.apply(fe, fe.items);
48594                         delete fe.items;
48595                     }
48596                      this.end();
48597                     continue;
48598                 }
48599                 
48600                 
48601                  
48602                 this.add(fe);
48603               //  console.log('adding ' + ar[i].xtype);
48604             }
48605             if (ar[i].xtype == 'Button') {  
48606                 //console.log('adding button');
48607                 //console.log(ar[i]);
48608                 this.addButton(ar[i]);
48609                 this.allItems.push(fe);
48610                 continue;
48611             }
48612             
48613             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48614                 alert('end is not supported on xtype any more, use items');
48615             //    this.end();
48616             //    //console.log('adding end');
48617             }
48618             
48619         }
48620         return ret;
48621     },
48622     
48623     /**
48624      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48625      * option "monitorValid"
48626      */
48627     startMonitoring : function(){
48628         if(!this.bound){
48629             this.bound = true;
48630             Roo.TaskMgr.start({
48631                 run : this.bindHandler,
48632                 interval : this.monitorPoll || 200,
48633                 scope: this
48634             });
48635         }
48636     },
48637
48638     /**
48639      * Stops monitoring of the valid state of this form
48640      */
48641     stopMonitoring : function(){
48642         this.bound = false;
48643     },
48644
48645     // private
48646     bindHandler : function(){
48647         if(!this.bound){
48648             return false; // stops binding
48649         }
48650         var valid = true;
48651         this.items.each(function(f){
48652             if(!f.isValid(true)){
48653                 valid = false;
48654                 return false;
48655             }
48656         });
48657         for(var i = 0, len = this.buttons.length; i < len; i++){
48658             var btn = this.buttons[i];
48659             if(btn.formBind === true && btn.disabled === valid){
48660                 btn.setDisabled(!valid);
48661             }
48662         }
48663         this.fireEvent('clientvalidation', this, valid);
48664     }
48665     
48666     
48667     
48668     
48669     
48670     
48671     
48672     
48673 });
48674
48675
48676 // back compat
48677 Roo.Form = Roo.form.Form;
48678 /*
48679  * Based on:
48680  * Ext JS Library 1.1.1
48681  * Copyright(c) 2006-2007, Ext JS, LLC.
48682  *
48683  * Originally Released Under LGPL - original licence link has changed is not relivant.
48684  *
48685  * Fork - LGPL
48686  * <script type="text/javascript">
48687  */
48688
48689 // as we use this in bootstrap.
48690 Roo.namespace('Roo.form');
48691  /**
48692  * @class Roo.form.Action
48693  * Internal Class used to handle form actions
48694  * @constructor
48695  * @param {Roo.form.BasicForm} el The form element or its id
48696  * @param {Object} config Configuration options
48697  */
48698
48699  
48700  
48701 // define the action interface
48702 Roo.form.Action = function(form, options){
48703     this.form = form;
48704     this.options = options || {};
48705 };
48706 /**
48707  * Client Validation Failed
48708  * @const 
48709  */
48710 Roo.form.Action.CLIENT_INVALID = 'client';
48711 /**
48712  * Server Validation Failed
48713  * @const 
48714  */
48715 Roo.form.Action.SERVER_INVALID = 'server';
48716  /**
48717  * Connect to Server Failed
48718  * @const 
48719  */
48720 Roo.form.Action.CONNECT_FAILURE = 'connect';
48721 /**
48722  * Reading Data from Server Failed
48723  * @const 
48724  */
48725 Roo.form.Action.LOAD_FAILURE = 'load';
48726
48727 Roo.form.Action.prototype = {
48728     type : 'default',
48729     failureType : undefined,
48730     response : undefined,
48731     result : undefined,
48732
48733     // interface method
48734     run : function(options){
48735
48736     },
48737
48738     // interface method
48739     success : function(response){
48740
48741     },
48742
48743     // interface method
48744     handleResponse : function(response){
48745
48746     },
48747
48748     // default connection failure
48749     failure : function(response){
48750         
48751         this.response = response;
48752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48753         this.form.afterAction(this, false);
48754     },
48755
48756     processResponse : function(response){
48757         this.response = response;
48758         if(!response.responseText){
48759             return true;
48760         }
48761         this.result = this.handleResponse(response);
48762         return this.result;
48763     },
48764
48765     // utility functions used internally
48766     getUrl : function(appendParams){
48767         var url = this.options.url || this.form.url || this.form.el.dom.action;
48768         if(appendParams){
48769             var p = this.getParams();
48770             if(p){
48771                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48772             }
48773         }
48774         return url;
48775     },
48776
48777     getMethod : function(){
48778         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48779     },
48780
48781     getParams : function(){
48782         var bp = this.form.baseParams;
48783         var p = this.options.params;
48784         if(p){
48785             if(typeof p == "object"){
48786                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48787             }else if(typeof p == 'string' && bp){
48788                 p += '&' + Roo.urlEncode(bp);
48789             }
48790         }else if(bp){
48791             p = Roo.urlEncode(bp);
48792         }
48793         return p;
48794     },
48795
48796     createCallback : function(){
48797         return {
48798             success: this.success,
48799             failure: this.failure,
48800             scope: this,
48801             timeout: (this.form.timeout*1000),
48802             upload: this.form.fileUpload ? this.success : undefined
48803         };
48804     }
48805 };
48806
48807 Roo.form.Action.Submit = function(form, options){
48808     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48809 };
48810
48811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48812     type : 'submit',
48813
48814     haveProgress : false,
48815     uploadComplete : false,
48816     
48817     // uploadProgress indicator.
48818     uploadProgress : function()
48819     {
48820         if (!this.form.progressUrl) {
48821             return;
48822         }
48823         
48824         if (!this.haveProgress) {
48825             Roo.MessageBox.progress("Uploading", "Uploading");
48826         }
48827         if (this.uploadComplete) {
48828            Roo.MessageBox.hide();
48829            return;
48830         }
48831         
48832         this.haveProgress = true;
48833    
48834         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48835         
48836         var c = new Roo.data.Connection();
48837         c.request({
48838             url : this.form.progressUrl,
48839             params: {
48840                 id : uid
48841             },
48842             method: 'GET',
48843             success : function(req){
48844                //console.log(data);
48845                 var rdata = false;
48846                 var edata;
48847                 try  {
48848                    rdata = Roo.decode(req.responseText)
48849                 } catch (e) {
48850                     Roo.log("Invalid data from server..");
48851                     Roo.log(edata);
48852                     return;
48853                 }
48854                 if (!rdata || !rdata.success) {
48855                     Roo.log(rdata);
48856                     Roo.MessageBox.alert(Roo.encode(rdata));
48857                     return;
48858                 }
48859                 var data = rdata.data;
48860                 
48861                 if (this.uploadComplete) {
48862                    Roo.MessageBox.hide();
48863                    return;
48864                 }
48865                    
48866                 if (data){
48867                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48868                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48869                     );
48870                 }
48871                 this.uploadProgress.defer(2000,this);
48872             },
48873        
48874             failure: function(data) {
48875                 Roo.log('progress url failed ');
48876                 Roo.log(data);
48877             },
48878             scope : this
48879         });
48880            
48881     },
48882     
48883     
48884     run : function()
48885     {
48886         // run get Values on the form, so it syncs any secondary forms.
48887         this.form.getValues();
48888         
48889         var o = this.options;
48890         var method = this.getMethod();
48891         var isPost = method == 'POST';
48892         if(o.clientValidation === false || this.form.isValid()){
48893             
48894             if (this.form.progressUrl) {
48895                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48896                     (new Date() * 1) + '' + Math.random());
48897                     
48898             } 
48899             
48900             
48901             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48902                 form:this.form.el.dom,
48903                 url:this.getUrl(!isPost),
48904                 method: method,
48905                 params:isPost ? this.getParams() : null,
48906                 isUpload: this.form.fileUpload,
48907                 formData : this.form.formData
48908             }));
48909             
48910             this.uploadProgress();
48911
48912         }else if (o.clientValidation !== false){ // client validation failed
48913             this.failureType = Roo.form.Action.CLIENT_INVALID;
48914             this.form.afterAction(this, false);
48915         }
48916     },
48917
48918     success : function(response)
48919     {
48920         this.uploadComplete= true;
48921         if (this.haveProgress) {
48922             Roo.MessageBox.hide();
48923         }
48924         
48925         
48926         var result = this.processResponse(response);
48927         if(result === true || result.success){
48928             this.form.afterAction(this, true);
48929             return;
48930         }
48931         if(result.errors){
48932             this.form.markInvalid(result.errors);
48933             this.failureType = Roo.form.Action.SERVER_INVALID;
48934         }
48935         this.form.afterAction(this, false);
48936     },
48937     failure : function(response)
48938     {
48939         this.uploadComplete= true;
48940         if (this.haveProgress) {
48941             Roo.MessageBox.hide();
48942         }
48943         
48944         this.response = response;
48945         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48946         this.form.afterAction(this, false);
48947     },
48948     
48949     handleResponse : function(response){
48950         if(this.form.errorReader){
48951             var rs = this.form.errorReader.read(response);
48952             var errors = [];
48953             if(rs.records){
48954                 for(var i = 0, len = rs.records.length; i < len; i++) {
48955                     var r = rs.records[i];
48956                     errors[i] = r.data;
48957                 }
48958             }
48959             if(errors.length < 1){
48960                 errors = null;
48961             }
48962             return {
48963                 success : rs.success,
48964                 errors : errors
48965             };
48966         }
48967         var ret = false;
48968         try {
48969             ret = Roo.decode(response.responseText);
48970         } catch (e) {
48971             ret = {
48972                 success: false,
48973                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48974                 errors : []
48975             };
48976         }
48977         return ret;
48978         
48979     }
48980 });
48981
48982
48983 Roo.form.Action.Load = function(form, options){
48984     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48985     this.reader = this.form.reader;
48986 };
48987
48988 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48989     type : 'load',
48990
48991     run : function(){
48992         
48993         Roo.Ajax.request(Roo.apply(
48994                 this.createCallback(), {
48995                     method:this.getMethod(),
48996                     url:this.getUrl(false),
48997                     params:this.getParams()
48998         }));
48999     },
49000
49001     success : function(response){
49002         
49003         var result = this.processResponse(response);
49004         if(result === true || !result.success || !result.data){
49005             this.failureType = Roo.form.Action.LOAD_FAILURE;
49006             this.form.afterAction(this, false);
49007             return;
49008         }
49009         this.form.clearInvalid();
49010         this.form.setValues(result.data);
49011         this.form.afterAction(this, true);
49012     },
49013
49014     handleResponse : function(response){
49015         if(this.form.reader){
49016             var rs = this.form.reader.read(response);
49017             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49018             return {
49019                 success : rs.success,
49020                 data : data
49021             };
49022         }
49023         return Roo.decode(response.responseText);
49024     }
49025 });
49026
49027 Roo.form.Action.ACTION_TYPES = {
49028     'load' : Roo.form.Action.Load,
49029     'submit' : Roo.form.Action.Submit
49030 };/*
49031  * Based on:
49032  * Ext JS Library 1.1.1
49033  * Copyright(c) 2006-2007, Ext JS, LLC.
49034  *
49035  * Originally Released Under LGPL - original licence link has changed is not relivant.
49036  *
49037  * Fork - LGPL
49038  * <script type="text/javascript">
49039  */
49040  
49041 /**
49042  * @class Roo.form.Layout
49043  * @extends Roo.Component
49044  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49045  * @constructor
49046  * @param {Object} config Configuration options
49047  */
49048 Roo.form.Layout = function(config){
49049     var xitems = [];
49050     if (config.items) {
49051         xitems = config.items;
49052         delete config.items;
49053     }
49054     Roo.form.Layout.superclass.constructor.call(this, config);
49055     this.stack = [];
49056     Roo.each(xitems, this.addxtype, this);
49057      
49058 };
49059
49060 Roo.extend(Roo.form.Layout, Roo.Component, {
49061     /**
49062      * @cfg {String/Object} autoCreate
49063      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49064      */
49065     /**
49066      * @cfg {String/Object/Function} style
49067      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49068      * a function which returns such a specification.
49069      */
49070     /**
49071      * @cfg {String} labelAlign
49072      * Valid values are "left," "top" and "right" (defaults to "left")
49073      */
49074     /**
49075      * @cfg {Number} labelWidth
49076      * Fixed width in pixels of all field labels (defaults to undefined)
49077      */
49078     /**
49079      * @cfg {Boolean} clear
49080      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49081      */
49082     clear : true,
49083     /**
49084      * @cfg {String} labelSeparator
49085      * The separator to use after field labels (defaults to ':')
49086      */
49087     labelSeparator : ':',
49088     /**
49089      * @cfg {Boolean} hideLabels
49090      * True to suppress the display of field labels in this layout (defaults to false)
49091      */
49092     hideLabels : false,
49093
49094     // private
49095     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49096     
49097     isLayout : true,
49098     
49099     // private
49100     onRender : function(ct, position){
49101         if(this.el){ // from markup
49102             this.el = Roo.get(this.el);
49103         }else {  // generate
49104             var cfg = this.getAutoCreate();
49105             this.el = ct.createChild(cfg, position);
49106         }
49107         if(this.style){
49108             this.el.applyStyles(this.style);
49109         }
49110         if(this.labelAlign){
49111             this.el.addClass('x-form-label-'+this.labelAlign);
49112         }
49113         if(this.hideLabels){
49114             this.labelStyle = "display:none";
49115             this.elementStyle = "padding-left:0;";
49116         }else{
49117             if(typeof this.labelWidth == 'number'){
49118                 this.labelStyle = "width:"+this.labelWidth+"px;";
49119                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49120             }
49121             if(this.labelAlign == 'top'){
49122                 this.labelStyle = "width:auto;";
49123                 this.elementStyle = "padding-left:0;";
49124             }
49125         }
49126         var stack = this.stack;
49127         var slen = stack.length;
49128         if(slen > 0){
49129             if(!this.fieldTpl){
49130                 var t = new Roo.Template(
49131                     '<div class="x-form-item {5}">',
49132                         '<label for="{0}" style="{2}">{1}{4}</label>',
49133                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49134                         '</div>',
49135                     '</div><div class="x-form-clear-left"></div>'
49136                 );
49137                 t.disableFormats = true;
49138                 t.compile();
49139                 Roo.form.Layout.prototype.fieldTpl = t;
49140             }
49141             for(var i = 0; i < slen; i++) {
49142                 if(stack[i].isFormField){
49143                     this.renderField(stack[i]);
49144                 }else{
49145                     this.renderComponent(stack[i]);
49146                 }
49147             }
49148         }
49149         if(this.clear){
49150             this.el.createChild({cls:'x-form-clear'});
49151         }
49152     },
49153
49154     // private
49155     renderField : function(f){
49156         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49157                f.id, //0
49158                f.fieldLabel, //1
49159                f.labelStyle||this.labelStyle||'', //2
49160                this.elementStyle||'', //3
49161                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49162                f.itemCls||this.itemCls||''  //5
49163        ], true).getPrevSibling());
49164     },
49165
49166     // private
49167     renderComponent : function(c){
49168         c.render(c.isLayout ? this.el : this.el.createChild());    
49169     },
49170     /**
49171      * Adds a object form elements (using the xtype property as the factory method.)
49172      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49173      * @param {Object} config 
49174      */
49175     addxtype : function(o)
49176     {
49177         // create the lement.
49178         o.form = this.form;
49179         var fe = Roo.factory(o, Roo.form);
49180         this.form.allItems.push(fe);
49181         this.stack.push(fe);
49182         
49183         if (fe.isFormField) {
49184             this.form.items.add(fe);
49185         }
49186          
49187         return fe;
49188     }
49189 });
49190
49191 /**
49192  * @class Roo.form.Column
49193  * @extends Roo.form.Layout
49194  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49195  * @constructor
49196  * @param {Object} config Configuration options
49197  */
49198 Roo.form.Column = function(config){
49199     Roo.form.Column.superclass.constructor.call(this, config);
49200 };
49201
49202 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49203     /**
49204      * @cfg {Number/String} width
49205      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49206      */
49207     /**
49208      * @cfg {String/Object} autoCreate
49209      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49210      */
49211
49212     // private
49213     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49214
49215     // private
49216     onRender : function(ct, position){
49217         Roo.form.Column.superclass.onRender.call(this, ct, position);
49218         if(this.width){
49219             this.el.setWidth(this.width);
49220         }
49221     }
49222 });
49223
49224
49225 /**
49226  * @class Roo.form.Row
49227  * @extends Roo.form.Layout
49228  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49229  * @constructor
49230  * @param {Object} config Configuration options
49231  */
49232
49233  
49234 Roo.form.Row = function(config){
49235     Roo.form.Row.superclass.constructor.call(this, config);
49236 };
49237  
49238 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49239       /**
49240      * @cfg {Number/String} width
49241      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49242      */
49243     /**
49244      * @cfg {Number/String} height
49245      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49246      */
49247     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49248     
49249     padWidth : 20,
49250     // private
49251     onRender : function(ct, position){
49252         //console.log('row render');
49253         if(!this.rowTpl){
49254             var t = new Roo.Template(
49255                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49256                     '<label for="{0}" style="{2}">{1}{4}</label>',
49257                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49258                     '</div>',
49259                 '</div>'
49260             );
49261             t.disableFormats = true;
49262             t.compile();
49263             Roo.form.Layout.prototype.rowTpl = t;
49264         }
49265         this.fieldTpl = this.rowTpl;
49266         
49267         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49268         var labelWidth = 100;
49269         
49270         if ((this.labelAlign != 'top')) {
49271             if (typeof this.labelWidth == 'number') {
49272                 labelWidth = this.labelWidth
49273             }
49274             this.padWidth =  20 + labelWidth;
49275             
49276         }
49277         
49278         Roo.form.Column.superclass.onRender.call(this, ct, position);
49279         if(this.width){
49280             this.el.setWidth(this.width);
49281         }
49282         if(this.height){
49283             this.el.setHeight(this.height);
49284         }
49285     },
49286     
49287     // private
49288     renderField : function(f){
49289         f.fieldEl = this.fieldTpl.append(this.el, [
49290                f.id, f.fieldLabel,
49291                f.labelStyle||this.labelStyle||'',
49292                this.elementStyle||'',
49293                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49294                f.itemCls||this.itemCls||'',
49295                f.width ? f.width + this.padWidth : 160 + this.padWidth
49296        ],true);
49297     }
49298 });
49299  
49300
49301 /**
49302  * @class Roo.form.FieldSet
49303  * @extends Roo.form.Layout
49304  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49305  * @constructor
49306  * @param {Object} config Configuration options
49307  */
49308 Roo.form.FieldSet = function(config){
49309     Roo.form.FieldSet.superclass.constructor.call(this, config);
49310 };
49311
49312 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49313     /**
49314      * @cfg {String} legend
49315      * The text to display as the legend for the FieldSet (defaults to '')
49316      */
49317     /**
49318      * @cfg {String/Object} autoCreate
49319      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49320      */
49321
49322     // private
49323     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49324
49325     // private
49326     onRender : function(ct, position){
49327         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49328         if(this.legend){
49329             this.setLegend(this.legend);
49330         }
49331     },
49332
49333     // private
49334     setLegend : function(text){
49335         if(this.rendered){
49336             this.el.child('legend').update(text);
49337         }
49338     }
49339 });/*
49340  * Based on:
49341  * Ext JS Library 1.1.1
49342  * Copyright(c) 2006-2007, Ext JS, LLC.
49343  *
49344  * Originally Released Under LGPL - original licence link has changed is not relivant.
49345  *
49346  * Fork - LGPL
49347  * <script type="text/javascript">
49348  */
49349 /**
49350  * @class Roo.form.VTypes
49351  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49352  * @singleton
49353  */
49354 Roo.form.VTypes = function(){
49355     // closure these in so they are only created once.
49356     var alpha = /^[a-zA-Z_]+$/;
49357     var alphanum = /^[a-zA-Z0-9_]+$/;
49358     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49359     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49360
49361     // All these messages and functions are configurable
49362     return {
49363         /**
49364          * The function used to validate email addresses
49365          * @param {String} value The email address
49366          */
49367         'email' : function(v){
49368             return email.test(v);
49369         },
49370         /**
49371          * The error text to display when the email validation function returns false
49372          * @type String
49373          */
49374         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49375         /**
49376          * The keystroke filter mask to be applied on email input
49377          * @type RegExp
49378          */
49379         'emailMask' : /[a-z0-9_\.\-@]/i,
49380
49381         /**
49382          * The function used to validate URLs
49383          * @param {String} value The URL
49384          */
49385         'url' : function(v){
49386             return url.test(v);
49387         },
49388         /**
49389          * The error text to display when the url validation function returns false
49390          * @type String
49391          */
49392         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49393         
49394         /**
49395          * The function used to validate alpha values
49396          * @param {String} value The value
49397          */
49398         'alpha' : function(v){
49399             return alpha.test(v);
49400         },
49401         /**
49402          * The error text to display when the alpha validation function returns false
49403          * @type String
49404          */
49405         'alphaText' : 'This field should only contain letters and _',
49406         /**
49407          * The keystroke filter mask to be applied on alpha input
49408          * @type RegExp
49409          */
49410         'alphaMask' : /[a-z_]/i,
49411
49412         /**
49413          * The function used to validate alphanumeric values
49414          * @param {String} value The value
49415          */
49416         'alphanum' : function(v){
49417             return alphanum.test(v);
49418         },
49419         /**
49420          * The error text to display when the alphanumeric validation function returns false
49421          * @type String
49422          */
49423         'alphanumText' : 'This field should only contain letters, numbers and _',
49424         /**
49425          * The keystroke filter mask to be applied on alphanumeric input
49426          * @type RegExp
49427          */
49428         'alphanumMask' : /[a-z0-9_]/i
49429     };
49430 }();//<script type="text/javascript">
49431
49432 /**
49433  * @class Roo.form.FCKeditor
49434  * @extends Roo.form.TextArea
49435  * Wrapper around the FCKEditor http://www.fckeditor.net
49436  * @constructor
49437  * Creates a new FCKeditor
49438  * @param {Object} config Configuration options
49439  */
49440 Roo.form.FCKeditor = function(config){
49441     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49442     this.addEvents({
49443          /**
49444          * @event editorinit
49445          * Fired when the editor is initialized - you can add extra handlers here..
49446          * @param {FCKeditor} this
49447          * @param {Object} the FCK object.
49448          */
49449         editorinit : true
49450     });
49451     
49452     
49453 };
49454 Roo.form.FCKeditor.editors = { };
49455 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49456 {
49457     //defaultAutoCreate : {
49458     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49459     //},
49460     // private
49461     /**
49462      * @cfg {Object} fck options - see fck manual for details.
49463      */
49464     fckconfig : false,
49465     
49466     /**
49467      * @cfg {Object} fck toolbar set (Basic or Default)
49468      */
49469     toolbarSet : 'Basic',
49470     /**
49471      * @cfg {Object} fck BasePath
49472      */ 
49473     basePath : '/fckeditor/',
49474     
49475     
49476     frame : false,
49477     
49478     value : '',
49479     
49480    
49481     onRender : function(ct, position)
49482     {
49483         if(!this.el){
49484             this.defaultAutoCreate = {
49485                 tag: "textarea",
49486                 style:"width:300px;height:60px;",
49487                 autocomplete: "new-password"
49488             };
49489         }
49490         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49491         /*
49492         if(this.grow){
49493             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49494             if(this.preventScrollbars){
49495                 this.el.setStyle("overflow", "hidden");
49496             }
49497             this.el.setHeight(this.growMin);
49498         }
49499         */
49500         //console.log('onrender' + this.getId() );
49501         Roo.form.FCKeditor.editors[this.getId()] = this;
49502          
49503
49504         this.replaceTextarea() ;
49505         
49506     },
49507     
49508     getEditor : function() {
49509         return this.fckEditor;
49510     },
49511     /**
49512      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49513      * @param {Mixed} value The value to set
49514      */
49515     
49516     
49517     setValue : function(value)
49518     {
49519         //console.log('setValue: ' + value);
49520         
49521         if(typeof(value) == 'undefined') { // not sure why this is happending...
49522             return;
49523         }
49524         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49525         
49526         //if(!this.el || !this.getEditor()) {
49527         //    this.value = value;
49528             //this.setValue.defer(100,this,[value]);    
49529         //    return;
49530         //} 
49531         
49532         if(!this.getEditor()) {
49533             return;
49534         }
49535         
49536         this.getEditor().SetData(value);
49537         
49538         //
49539
49540     },
49541
49542     /**
49543      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49544      * @return {Mixed} value The field value
49545      */
49546     getValue : function()
49547     {
49548         
49549         if (this.frame && this.frame.dom.style.display == 'none') {
49550             return Roo.form.FCKeditor.superclass.getValue.call(this);
49551         }
49552         
49553         if(!this.el || !this.getEditor()) {
49554            
49555            // this.getValue.defer(100,this); 
49556             return this.value;
49557         }
49558        
49559         
49560         var value=this.getEditor().GetData();
49561         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49562         return Roo.form.FCKeditor.superclass.getValue.call(this);
49563         
49564
49565     },
49566
49567     /**
49568      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49569      * @return {Mixed} value The field value
49570      */
49571     getRawValue : function()
49572     {
49573         if (this.frame && this.frame.dom.style.display == 'none') {
49574             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49575         }
49576         
49577         if(!this.el || !this.getEditor()) {
49578             //this.getRawValue.defer(100,this); 
49579             return this.value;
49580             return;
49581         }
49582         
49583         
49584         
49585         var value=this.getEditor().GetData();
49586         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49587         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49588          
49589     },
49590     
49591     setSize : function(w,h) {
49592         
49593         
49594         
49595         //if (this.frame && this.frame.dom.style.display == 'none') {
49596         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49597         //    return;
49598         //}
49599         //if(!this.el || !this.getEditor()) {
49600         //    this.setSize.defer(100,this, [w,h]); 
49601         //    return;
49602         //}
49603         
49604         
49605         
49606         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49607         
49608         this.frame.dom.setAttribute('width', w);
49609         this.frame.dom.setAttribute('height', h);
49610         this.frame.setSize(w,h);
49611         
49612     },
49613     
49614     toggleSourceEdit : function(value) {
49615         
49616       
49617          
49618         this.el.dom.style.display = value ? '' : 'none';
49619         this.frame.dom.style.display = value ?  'none' : '';
49620         
49621     },
49622     
49623     
49624     focus: function(tag)
49625     {
49626         if (this.frame.dom.style.display == 'none') {
49627             return Roo.form.FCKeditor.superclass.focus.call(this);
49628         }
49629         if(!this.el || !this.getEditor()) {
49630             this.focus.defer(100,this, [tag]); 
49631             return;
49632         }
49633         
49634         
49635         
49636         
49637         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49638         this.getEditor().Focus();
49639         if (tgs.length) {
49640             if (!this.getEditor().Selection.GetSelection()) {
49641                 this.focus.defer(100,this, [tag]); 
49642                 return;
49643             }
49644             
49645             
49646             var r = this.getEditor().EditorDocument.createRange();
49647             r.setStart(tgs[0],0);
49648             r.setEnd(tgs[0],0);
49649             this.getEditor().Selection.GetSelection().removeAllRanges();
49650             this.getEditor().Selection.GetSelection().addRange(r);
49651             this.getEditor().Focus();
49652         }
49653         
49654     },
49655     
49656     
49657     
49658     replaceTextarea : function()
49659     {
49660         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49661             return ;
49662         }
49663         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49664         //{
49665             // We must check the elements firstly using the Id and then the name.
49666         var oTextarea = document.getElementById( this.getId() );
49667         
49668         var colElementsByName = document.getElementsByName( this.getId() ) ;
49669          
49670         oTextarea.style.display = 'none' ;
49671
49672         if ( oTextarea.tabIndex ) {            
49673             this.TabIndex = oTextarea.tabIndex ;
49674         }
49675         
49676         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49677         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49678         this.frame = Roo.get(this.getId() + '___Frame')
49679     },
49680     
49681     _getConfigHtml : function()
49682     {
49683         var sConfig = '' ;
49684
49685         for ( var o in this.fckconfig ) {
49686             sConfig += sConfig.length > 0  ? '&amp;' : '';
49687             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49688         }
49689
49690         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49691     },
49692     
49693     
49694     _getIFrameHtml : function()
49695     {
49696         var sFile = 'fckeditor.html' ;
49697         /* no idea what this is about..
49698         try
49699         {
49700             if ( (/fcksource=true/i).test( window.top.location.search ) )
49701                 sFile = 'fckeditor.original.html' ;
49702         }
49703         catch (e) { 
49704         */
49705
49706         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49707         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49708         
49709         
49710         var html = '<iframe id="' + this.getId() +
49711             '___Frame" src="' + sLink +
49712             '" width="' + this.width +
49713             '" height="' + this.height + '"' +
49714             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49715             ' frameborder="0" scrolling="no"></iframe>' ;
49716
49717         return html ;
49718     },
49719     
49720     _insertHtmlBefore : function( html, element )
49721     {
49722         if ( element.insertAdjacentHTML )       {
49723             // IE
49724             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49725         } else { // Gecko
49726             var oRange = document.createRange() ;
49727             oRange.setStartBefore( element ) ;
49728             var oFragment = oRange.createContextualFragment( html );
49729             element.parentNode.insertBefore( oFragment, element ) ;
49730         }
49731     }
49732     
49733     
49734   
49735     
49736     
49737     
49738     
49739
49740 });
49741
49742 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49743
49744 function FCKeditor_OnComplete(editorInstance){
49745     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49746     f.fckEditor = editorInstance;
49747     //console.log("loaded");
49748     f.fireEvent('editorinit', f, editorInstance);
49749
49750   
49751
49752  
49753
49754
49755
49756
49757
49758
49759
49760
49761
49762
49763
49764
49765
49766
49767
49768 //<script type="text/javascript">
49769 /**
49770  * @class Roo.form.GridField
49771  * @extends Roo.form.Field
49772  * Embed a grid (or editable grid into a form)
49773  * STATUS ALPHA
49774  * 
49775  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49776  * it needs 
49777  * xgrid.store = Roo.data.Store
49778  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49779  * xgrid.store.reader = Roo.data.JsonReader 
49780  * 
49781  * 
49782  * @constructor
49783  * Creates a new GridField
49784  * @param {Object} config Configuration options
49785  */
49786 Roo.form.GridField = function(config){
49787     Roo.form.GridField.superclass.constructor.call(this, config);
49788      
49789 };
49790
49791 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49792     /**
49793      * @cfg {Number} width  - used to restrict width of grid..
49794      */
49795     width : 100,
49796     /**
49797      * @cfg {Number} height - used to restrict height of grid..
49798      */
49799     height : 50,
49800      /**
49801      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49802          * 
49803          *}
49804      */
49805     xgrid : false, 
49806     /**
49807      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49808      * {tag: "input", type: "checkbox", autocomplete: "off"})
49809      */
49810    // defaultAutoCreate : { tag: 'div' },
49811     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49812     /**
49813      * @cfg {String} addTitle Text to include for adding a title.
49814      */
49815     addTitle : false,
49816     //
49817     onResize : function(){
49818         Roo.form.Field.superclass.onResize.apply(this, arguments);
49819     },
49820
49821     initEvents : function(){
49822         // Roo.form.Checkbox.superclass.initEvents.call(this);
49823         // has no events...
49824        
49825     },
49826
49827
49828     getResizeEl : function(){
49829         return this.wrap;
49830     },
49831
49832     getPositionEl : function(){
49833         return this.wrap;
49834     },
49835
49836     // private
49837     onRender : function(ct, position){
49838         
49839         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49840         var style = this.style;
49841         delete this.style;
49842         
49843         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49844         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49845         this.viewEl = this.wrap.createChild({ tag: 'div' });
49846         if (style) {
49847             this.viewEl.applyStyles(style);
49848         }
49849         if (this.width) {
49850             this.viewEl.setWidth(this.width);
49851         }
49852         if (this.height) {
49853             this.viewEl.setHeight(this.height);
49854         }
49855         //if(this.inputValue !== undefined){
49856         //this.setValue(this.value);
49857         
49858         
49859         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49860         
49861         
49862         this.grid.render();
49863         this.grid.getDataSource().on('remove', this.refreshValue, this);
49864         this.grid.getDataSource().on('update', this.refreshValue, this);
49865         this.grid.on('afteredit', this.refreshValue, this);
49866  
49867     },
49868      
49869     
49870     /**
49871      * Sets the value of the item. 
49872      * @param {String} either an object  or a string..
49873      */
49874     setValue : function(v){
49875         //this.value = v;
49876         v = v || []; // empty set..
49877         // this does not seem smart - it really only affects memoryproxy grids..
49878         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49879             var ds = this.grid.getDataSource();
49880             // assumes a json reader..
49881             var data = {}
49882             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49883             ds.loadData( data);
49884         }
49885         // clear selection so it does not get stale.
49886         if (this.grid.sm) { 
49887             this.grid.sm.clearSelections();
49888         }
49889         
49890         Roo.form.GridField.superclass.setValue.call(this, v);
49891         this.refreshValue();
49892         // should load data in the grid really....
49893     },
49894     
49895     // private
49896     refreshValue: function() {
49897          var val = [];
49898         this.grid.getDataSource().each(function(r) {
49899             val.push(r.data);
49900         });
49901         this.el.dom.value = Roo.encode(val);
49902     }
49903     
49904      
49905     
49906     
49907 });/*
49908  * Based on:
49909  * Ext JS Library 1.1.1
49910  * Copyright(c) 2006-2007, Ext JS, LLC.
49911  *
49912  * Originally Released Under LGPL - original licence link has changed is not relivant.
49913  *
49914  * Fork - LGPL
49915  * <script type="text/javascript">
49916  */
49917 /**
49918  * @class Roo.form.DisplayField
49919  * @extends Roo.form.Field
49920  * A generic Field to display non-editable data.
49921  * @cfg {Boolean} closable (true|false) default false
49922  * @constructor
49923  * Creates a new Display Field item.
49924  * @param {Object} config Configuration options
49925  */
49926 Roo.form.DisplayField = function(config){
49927     Roo.form.DisplayField.superclass.constructor.call(this, config);
49928     
49929     this.addEvents({
49930         /**
49931          * @event close
49932          * Fires after the click the close btn
49933              * @param {Roo.form.DisplayField} this
49934              */
49935         close : true
49936     });
49937 };
49938
49939 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49940     inputType:      'hidden',
49941     allowBlank:     true,
49942     readOnly:         true,
49943     
49944  
49945     /**
49946      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49947      */
49948     focusClass : undefined,
49949     /**
49950      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49951      */
49952     fieldClass: 'x-form-field',
49953     
49954      /**
49955      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49956      */
49957     valueRenderer: undefined,
49958     
49959     width: 100,
49960     /**
49961      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49962      * {tag: "input", type: "checkbox", autocomplete: "off"})
49963      */
49964      
49965  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49966  
49967     closable : false,
49968     
49969     onResize : function(){
49970         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49971         
49972     },
49973
49974     initEvents : function(){
49975         // Roo.form.Checkbox.superclass.initEvents.call(this);
49976         // has no events...
49977         
49978         if(this.closable){
49979             this.closeEl.on('click', this.onClose, this);
49980         }
49981        
49982     },
49983
49984
49985     getResizeEl : function(){
49986         return this.wrap;
49987     },
49988
49989     getPositionEl : function(){
49990         return this.wrap;
49991     },
49992
49993     // private
49994     onRender : function(ct, position){
49995         
49996         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49997         //if(this.inputValue !== undefined){
49998         this.wrap = this.el.wrap();
49999         
50000         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50001         
50002         if(this.closable){
50003             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50004         }
50005         
50006         if (this.bodyStyle) {
50007             this.viewEl.applyStyles(this.bodyStyle);
50008         }
50009         //this.viewEl.setStyle('padding', '2px');
50010         
50011         this.setValue(this.value);
50012         
50013     },
50014 /*
50015     // private
50016     initValue : Roo.emptyFn,
50017
50018   */
50019
50020         // private
50021     onClick : function(){
50022         
50023     },
50024
50025     /**
50026      * Sets the checked state of the checkbox.
50027      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50028      */
50029     setValue : function(v){
50030         this.value = v;
50031         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50032         // this might be called before we have a dom element..
50033         if (!this.viewEl) {
50034             return;
50035         }
50036         this.viewEl.dom.innerHTML = html;
50037         Roo.form.DisplayField.superclass.setValue.call(this, v);
50038
50039     },
50040     
50041     onClose : function(e)
50042     {
50043         e.preventDefault();
50044         
50045         this.fireEvent('close', this);
50046     }
50047 });/*
50048  * 
50049  * Licence- LGPL
50050  * 
50051  */
50052
50053 /**
50054  * @class Roo.form.DayPicker
50055  * @extends Roo.form.Field
50056  * A Day picker show [M] [T] [W] ....
50057  * @constructor
50058  * Creates a new Day Picker
50059  * @param {Object} config Configuration options
50060  */
50061 Roo.form.DayPicker= function(config){
50062     Roo.form.DayPicker.superclass.constructor.call(this, config);
50063      
50064 };
50065
50066 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50067     /**
50068      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50069      */
50070     focusClass : undefined,
50071     /**
50072      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50073      */
50074     fieldClass: "x-form-field",
50075    
50076     /**
50077      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50078      * {tag: "input", type: "checkbox", autocomplete: "off"})
50079      */
50080     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50081     
50082    
50083     actionMode : 'viewEl', 
50084     //
50085     // private
50086  
50087     inputType : 'hidden',
50088     
50089      
50090     inputElement: false, // real input element?
50091     basedOn: false, // ????
50092     
50093     isFormField: true, // not sure where this is needed!!!!
50094
50095     onResize : function(){
50096         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50097         if(!this.boxLabel){
50098             this.el.alignTo(this.wrap, 'c-c');
50099         }
50100     },
50101
50102     initEvents : function(){
50103         Roo.form.Checkbox.superclass.initEvents.call(this);
50104         this.el.on("click", this.onClick,  this);
50105         this.el.on("change", this.onClick,  this);
50106     },
50107
50108
50109     getResizeEl : function(){
50110         return this.wrap;
50111     },
50112
50113     getPositionEl : function(){
50114         return this.wrap;
50115     },
50116
50117     
50118     // private
50119     onRender : function(ct, position){
50120         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50121        
50122         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50123         
50124         var r1 = '<table><tr>';
50125         var r2 = '<tr class="x-form-daypick-icons">';
50126         for (var i=0; i < 7; i++) {
50127             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50128             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50129         }
50130         
50131         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50132         viewEl.select('img').on('click', this.onClick, this);
50133         this.viewEl = viewEl;   
50134         
50135         
50136         // this will not work on Chrome!!!
50137         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50138         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50139         
50140         
50141           
50142
50143     },
50144
50145     // private
50146     initValue : Roo.emptyFn,
50147
50148     /**
50149      * Returns the checked state of the checkbox.
50150      * @return {Boolean} True if checked, else false
50151      */
50152     getValue : function(){
50153         return this.el.dom.value;
50154         
50155     },
50156
50157         // private
50158     onClick : function(e){ 
50159         //this.setChecked(!this.checked);
50160         Roo.get(e.target).toggleClass('x-menu-item-checked');
50161         this.refreshValue();
50162         //if(this.el.dom.checked != this.checked){
50163         //    this.setValue(this.el.dom.checked);
50164        // }
50165     },
50166     
50167     // private
50168     refreshValue : function()
50169     {
50170         var val = '';
50171         this.viewEl.select('img',true).each(function(e,i,n)  {
50172             val += e.is(".x-menu-item-checked") ? String(n) : '';
50173         });
50174         this.setValue(val, true);
50175     },
50176
50177     /**
50178      * Sets the checked state of the checkbox.
50179      * On is always based on a string comparison between inputValue and the param.
50180      * @param {Boolean/String} value - the value to set 
50181      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50182      */
50183     setValue : function(v,suppressEvent){
50184         if (!this.el.dom) {
50185             return;
50186         }
50187         var old = this.el.dom.value ;
50188         this.el.dom.value = v;
50189         if (suppressEvent) {
50190             return ;
50191         }
50192          
50193         // update display..
50194         this.viewEl.select('img',true).each(function(e,i,n)  {
50195             
50196             var on = e.is(".x-menu-item-checked");
50197             var newv = v.indexOf(String(n)) > -1;
50198             if (on != newv) {
50199                 e.toggleClass('x-menu-item-checked');
50200             }
50201             
50202         });
50203         
50204         
50205         this.fireEvent('change', this, v, old);
50206         
50207         
50208     },
50209    
50210     // handle setting of hidden value by some other method!!?!?
50211     setFromHidden: function()
50212     {
50213         if(!this.el){
50214             return;
50215         }
50216         //console.log("SET FROM HIDDEN");
50217         //alert('setFrom hidden');
50218         this.setValue(this.el.dom.value);
50219     },
50220     
50221     onDestroy : function()
50222     {
50223         if(this.viewEl){
50224             Roo.get(this.viewEl).remove();
50225         }
50226          
50227         Roo.form.DayPicker.superclass.onDestroy.call(this);
50228     }
50229
50230 });/*
50231  * RooJS Library 1.1.1
50232  * Copyright(c) 2008-2011  Alan Knowles
50233  *
50234  * License - LGPL
50235  */
50236  
50237
50238 /**
50239  * @class Roo.form.ComboCheck
50240  * @extends Roo.form.ComboBox
50241  * A combobox for multiple select items.
50242  *
50243  * FIXME - could do with a reset button..
50244  * 
50245  * @constructor
50246  * Create a new ComboCheck
50247  * @param {Object} config Configuration options
50248  */
50249 Roo.form.ComboCheck = function(config){
50250     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50251     // should verify some data...
50252     // like
50253     // hiddenName = required..
50254     // displayField = required
50255     // valudField == required
50256     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50257     var _t = this;
50258     Roo.each(req, function(e) {
50259         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50260             throw "Roo.form.ComboCheck : missing value for: " + e;
50261         }
50262     });
50263     
50264     
50265 };
50266
50267 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50268      
50269      
50270     editable : false,
50271      
50272     selectedClass: 'x-menu-item-checked', 
50273     
50274     // private
50275     onRender : function(ct, position){
50276         var _t = this;
50277         
50278         
50279         
50280         if(!this.tpl){
50281             var cls = 'x-combo-list';
50282
50283             
50284             this.tpl =  new Roo.Template({
50285                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50286                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50287                    '<span>{' + this.displayField + '}</span>' +
50288                     '</div>' 
50289                 
50290             });
50291         }
50292  
50293         
50294         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50295         this.view.singleSelect = false;
50296         this.view.multiSelect = true;
50297         this.view.toggleSelect = true;
50298         this.pageTb.add(new Roo.Toolbar.Fill(), {
50299             
50300             text: 'Done',
50301             handler: function()
50302             {
50303                 _t.collapse();
50304             }
50305         });
50306     },
50307     
50308     onViewOver : function(e, t){
50309         // do nothing...
50310         return;
50311         
50312     },
50313     
50314     onViewClick : function(doFocus,index){
50315         return;
50316         
50317     },
50318     select: function () {
50319         //Roo.log("SELECT CALLED");
50320     },
50321      
50322     selectByValue : function(xv, scrollIntoView){
50323         var ar = this.getValueArray();
50324         var sels = [];
50325         
50326         Roo.each(ar, function(v) {
50327             if(v === undefined || v === null){
50328                 return;
50329             }
50330             var r = this.findRecord(this.valueField, v);
50331             if(r){
50332                 sels.push(this.store.indexOf(r))
50333                 
50334             }
50335         },this);
50336         this.view.select(sels);
50337         return false;
50338     },
50339     
50340     
50341     
50342     onSelect : function(record, index){
50343        // Roo.log("onselect Called");
50344        // this is only called by the clear button now..
50345         this.view.clearSelections();
50346         this.setValue('[]');
50347         if (this.value != this.valueBefore) {
50348             this.fireEvent('change', this, this.value, this.valueBefore);
50349             this.valueBefore = this.value;
50350         }
50351     },
50352     getValueArray : function()
50353     {
50354         var ar = [] ;
50355         
50356         try {
50357             //Roo.log(this.value);
50358             if (typeof(this.value) == 'undefined') {
50359                 return [];
50360             }
50361             var ar = Roo.decode(this.value);
50362             return  ar instanceof Array ? ar : []; //?? valid?
50363             
50364         } catch(e) {
50365             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50366             return [];
50367         }
50368          
50369     },
50370     expand : function ()
50371     {
50372         
50373         Roo.form.ComboCheck.superclass.expand.call(this);
50374         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50375         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50376         
50377
50378     },
50379     
50380     collapse : function(){
50381         Roo.form.ComboCheck.superclass.collapse.call(this);
50382         var sl = this.view.getSelectedIndexes();
50383         var st = this.store;
50384         var nv = [];
50385         var tv = [];
50386         var r;
50387         Roo.each(sl, function(i) {
50388             r = st.getAt(i);
50389             nv.push(r.get(this.valueField));
50390         },this);
50391         this.setValue(Roo.encode(nv));
50392         if (this.value != this.valueBefore) {
50393
50394             this.fireEvent('change', this, this.value, this.valueBefore);
50395             this.valueBefore = this.value;
50396         }
50397         
50398     },
50399     
50400     setValue : function(v){
50401         // Roo.log(v);
50402         this.value = v;
50403         
50404         var vals = this.getValueArray();
50405         var tv = [];
50406         Roo.each(vals, function(k) {
50407             var r = this.findRecord(this.valueField, k);
50408             if(r){
50409                 tv.push(r.data[this.displayField]);
50410             }else if(this.valueNotFoundText !== undefined){
50411                 tv.push( this.valueNotFoundText );
50412             }
50413         },this);
50414        // Roo.log(tv);
50415         
50416         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50417         this.hiddenField.value = v;
50418         this.value = v;
50419     }
50420     
50421 });/*
50422  * Based on:
50423  * Ext JS Library 1.1.1
50424  * Copyright(c) 2006-2007, Ext JS, LLC.
50425  *
50426  * Originally Released Under LGPL - original licence link has changed is not relivant.
50427  *
50428  * Fork - LGPL
50429  * <script type="text/javascript">
50430  */
50431  
50432 /**
50433  * @class Roo.form.Signature
50434  * @extends Roo.form.Field
50435  * Signature field.  
50436  * @constructor
50437  * 
50438  * @param {Object} config Configuration options
50439  */
50440
50441 Roo.form.Signature = function(config){
50442     Roo.form.Signature.superclass.constructor.call(this, config);
50443     
50444     this.addEvents({// not in used??
50445          /**
50446          * @event confirm
50447          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50448              * @param {Roo.form.Signature} combo This combo box
50449              */
50450         'confirm' : true,
50451         /**
50452          * @event reset
50453          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50454              * @param {Roo.form.ComboBox} combo This combo box
50455              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50456              */
50457         'reset' : true
50458     });
50459 };
50460
50461 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50462     /**
50463      * @cfg {Object} labels Label to use when rendering a form.
50464      * defaults to 
50465      * labels : { 
50466      *      clear : "Clear",
50467      *      confirm : "Confirm"
50468      *  }
50469      */
50470     labels : { 
50471         clear : "Clear",
50472         confirm : "Confirm"
50473     },
50474     /**
50475      * @cfg {Number} width The signature panel width (defaults to 300)
50476      */
50477     width: 300,
50478     /**
50479      * @cfg {Number} height The signature panel height (defaults to 100)
50480      */
50481     height : 100,
50482     /**
50483      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50484      */
50485     allowBlank : false,
50486     
50487     //private
50488     // {Object} signPanel The signature SVG panel element (defaults to {})
50489     signPanel : {},
50490     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50491     isMouseDown : false,
50492     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50493     isConfirmed : false,
50494     // {String} signatureTmp SVG mapping string (defaults to empty string)
50495     signatureTmp : '',
50496     
50497     
50498     defaultAutoCreate : { // modified by initCompnoent..
50499         tag: "input",
50500         type:"hidden"
50501     },
50502
50503     // private
50504     onRender : function(ct, position){
50505         
50506         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50507         
50508         this.wrap = this.el.wrap({
50509             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50510         });
50511         
50512         this.createToolbar(this);
50513         this.signPanel = this.wrap.createChild({
50514                 tag: 'div',
50515                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50516             }, this.el
50517         );
50518             
50519         this.svgID = Roo.id();
50520         this.svgEl = this.signPanel.createChild({
50521               xmlns : 'http://www.w3.org/2000/svg',
50522               tag : 'svg',
50523               id : this.svgID + "-svg",
50524               width: this.width,
50525               height: this.height,
50526               viewBox: '0 0 '+this.width+' '+this.height,
50527               cn : [
50528                 {
50529                     tag: "rect",
50530                     id: this.svgID + "-svg-r",
50531                     width: this.width,
50532                     height: this.height,
50533                     fill: "#ffa"
50534                 },
50535                 {
50536                     tag: "line",
50537                     id: this.svgID + "-svg-l",
50538                     x1: "0", // start
50539                     y1: (this.height*0.8), // start set the line in 80% of height
50540                     x2: this.width, // end
50541                     y2: (this.height*0.8), // end set the line in 80% of height
50542                     'stroke': "#666",
50543                     'stroke-width': "1",
50544                     'stroke-dasharray': "3",
50545                     'shape-rendering': "crispEdges",
50546                     'pointer-events': "none"
50547                 },
50548                 {
50549                     tag: "path",
50550                     id: this.svgID + "-svg-p",
50551                     'stroke': "navy",
50552                     'stroke-width': "3",
50553                     'fill': "none",
50554                     'pointer-events': 'none'
50555                 }
50556               ]
50557         });
50558         this.createSVG();
50559         this.svgBox = this.svgEl.dom.getScreenCTM();
50560     },
50561     createSVG : function(){ 
50562         var svg = this.signPanel;
50563         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50564         var t = this;
50565
50566         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50567         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50568         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50569         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50570         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50571         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50572         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50573         
50574     },
50575     isTouchEvent : function(e){
50576         return e.type.match(/^touch/);
50577     },
50578     getCoords : function (e) {
50579         var pt    = this.svgEl.dom.createSVGPoint();
50580         pt.x = e.clientX; 
50581         pt.y = e.clientY;
50582         if (this.isTouchEvent(e)) {
50583             pt.x =  e.targetTouches[0].clientX;
50584             pt.y = e.targetTouches[0].clientY;
50585         }
50586         var a = this.svgEl.dom.getScreenCTM();
50587         var b = a.inverse();
50588         var mx = pt.matrixTransform(b);
50589         return mx.x + ',' + mx.y;
50590     },
50591     //mouse event headler 
50592     down : function (e) {
50593         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50594         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50595         
50596         this.isMouseDown = true;
50597         
50598         e.preventDefault();
50599     },
50600     move : function (e) {
50601         if (this.isMouseDown) {
50602             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50603             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50604         }
50605         
50606         e.preventDefault();
50607     },
50608     up : function (e) {
50609         this.isMouseDown = false;
50610         var sp = this.signatureTmp.split(' ');
50611         
50612         if(sp.length > 1){
50613             if(!sp[sp.length-2].match(/^L/)){
50614                 sp.pop();
50615                 sp.pop();
50616                 sp.push("");
50617                 this.signatureTmp = sp.join(" ");
50618             }
50619         }
50620         if(this.getValue() != this.signatureTmp){
50621             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50622             this.isConfirmed = false;
50623         }
50624         e.preventDefault();
50625     },
50626     
50627     /**
50628      * Protected method that will not generally be called directly. It
50629      * is called when the editor creates its toolbar. Override this method if you need to
50630      * add custom toolbar buttons.
50631      * @param {HtmlEditor} editor
50632      */
50633     createToolbar : function(editor){
50634          function btn(id, toggle, handler){
50635             var xid = fid + '-'+ id ;
50636             return {
50637                 id : xid,
50638                 cmd : id,
50639                 cls : 'x-btn-icon x-edit-'+id,
50640                 enableToggle:toggle !== false,
50641                 scope: editor, // was editor...
50642                 handler:handler||editor.relayBtnCmd,
50643                 clickEvent:'mousedown',
50644                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50645                 tabIndex:-1
50646             };
50647         }
50648         
50649         
50650         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50651         this.tb = tb;
50652         this.tb.add(
50653            {
50654                 cls : ' x-signature-btn x-signature-'+id,
50655                 scope: editor, // was editor...
50656                 handler: this.reset,
50657                 clickEvent:'mousedown',
50658                 text: this.labels.clear
50659             },
50660             {
50661                  xtype : 'Fill',
50662                  xns: Roo.Toolbar
50663             }, 
50664             {
50665                 cls : '  x-signature-btn x-signature-'+id,
50666                 scope: editor, // was editor...
50667                 handler: this.confirmHandler,
50668                 clickEvent:'mousedown',
50669                 text: this.labels.confirm
50670             }
50671         );
50672     
50673     },
50674     //public
50675     /**
50676      * when user is clicked confirm then show this image.....
50677      * 
50678      * @return {String} Image Data URI
50679      */
50680     getImageDataURI : function(){
50681         var svg = this.svgEl.dom.parentNode.innerHTML;
50682         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50683         return src; 
50684     },
50685     /**
50686      * 
50687      * @return {Boolean} this.isConfirmed
50688      */
50689     getConfirmed : function(){
50690         return this.isConfirmed;
50691     },
50692     /**
50693      * 
50694      * @return {Number} this.width
50695      */
50696     getWidth : function(){
50697         return this.width;
50698     },
50699     /**
50700      * 
50701      * @return {Number} this.height
50702      */
50703     getHeight : function(){
50704         return this.height;
50705     },
50706     // private
50707     getSignature : function(){
50708         return this.signatureTmp;
50709     },
50710     // private
50711     reset : function(){
50712         this.signatureTmp = '';
50713         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50714         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50715         this.isConfirmed = false;
50716         Roo.form.Signature.superclass.reset.call(this);
50717     },
50718     setSignature : function(s){
50719         this.signatureTmp = s;
50720         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50721         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50722         this.setValue(s);
50723         this.isConfirmed = false;
50724         Roo.form.Signature.superclass.reset.call(this);
50725     }, 
50726     test : function(){
50727 //        Roo.log(this.signPanel.dom.contentWindow.up())
50728     },
50729     //private
50730     setConfirmed : function(){
50731         
50732         
50733         
50734 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50735     },
50736     // private
50737     confirmHandler : function(){
50738         if(!this.getSignature()){
50739             return;
50740         }
50741         
50742         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50743         this.setValue(this.getSignature());
50744         this.isConfirmed = true;
50745         
50746         this.fireEvent('confirm', this);
50747     },
50748     // private
50749     // Subclasses should provide the validation implementation by overriding this
50750     validateValue : function(value){
50751         if(this.allowBlank){
50752             return true;
50753         }
50754         
50755         if(this.isConfirmed){
50756             return true;
50757         }
50758         return false;
50759     }
50760 });/*
50761  * Based on:
50762  * Ext JS Library 1.1.1
50763  * Copyright(c) 2006-2007, Ext JS, LLC.
50764  *
50765  * Originally Released Under LGPL - original licence link has changed is not relivant.
50766  *
50767  * Fork - LGPL
50768  * <script type="text/javascript">
50769  */
50770  
50771
50772 /**
50773  * @class Roo.form.ComboBox
50774  * @extends Roo.form.TriggerField
50775  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50776  * @constructor
50777  * Create a new ComboBox.
50778  * @param {Object} config Configuration options
50779  */
50780 Roo.form.Select = function(config){
50781     Roo.form.Select.superclass.constructor.call(this, config);
50782      
50783 };
50784
50785 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50786     /**
50787      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50788      */
50789     /**
50790      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50791      * rendering into an Roo.Editor, defaults to false)
50792      */
50793     /**
50794      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50795      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50796      */
50797     /**
50798      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50799      */
50800     /**
50801      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50802      * the dropdown list (defaults to undefined, with no header element)
50803      */
50804
50805      /**
50806      * @cfg {String/Roo.Template} tpl The template to use to render the output
50807      */
50808      
50809     // private
50810     defaultAutoCreate : {tag: "select"  },
50811     /**
50812      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50813      */
50814     listWidth: undefined,
50815     /**
50816      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50817      * mode = 'remote' or 'text' if mode = 'local')
50818      */
50819     displayField: undefined,
50820     /**
50821      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50822      * mode = 'remote' or 'value' if mode = 'local'). 
50823      * Note: use of a valueField requires the user make a selection
50824      * in order for a value to be mapped.
50825      */
50826     valueField: undefined,
50827     
50828     
50829     /**
50830      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50831      * field's data value (defaults to the underlying DOM element's name)
50832      */
50833     hiddenName: undefined,
50834     /**
50835      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50836      */
50837     listClass: '',
50838     /**
50839      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50840      */
50841     selectedClass: 'x-combo-selected',
50842     /**
50843      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50844      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50845      * which displays a downward arrow icon).
50846      */
50847     triggerClass : 'x-form-arrow-trigger',
50848     /**
50849      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50850      */
50851     shadow:'sides',
50852     /**
50853      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50854      * anchor positions (defaults to 'tl-bl')
50855      */
50856     listAlign: 'tl-bl?',
50857     /**
50858      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50859      */
50860     maxHeight: 300,
50861     /**
50862      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50863      * query specified by the allQuery config option (defaults to 'query')
50864      */
50865     triggerAction: 'query',
50866     /**
50867      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50868      * (defaults to 4, does not apply if editable = false)
50869      */
50870     minChars : 4,
50871     /**
50872      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50873      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50874      */
50875     typeAhead: false,
50876     /**
50877      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50878      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50879      */
50880     queryDelay: 500,
50881     /**
50882      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50883      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50884      */
50885     pageSize: 0,
50886     /**
50887      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50888      * when editable = true (defaults to false)
50889      */
50890     selectOnFocus:false,
50891     /**
50892      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50893      */
50894     queryParam: 'query',
50895     /**
50896      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50897      * when mode = 'remote' (defaults to 'Loading...')
50898      */
50899     loadingText: 'Loading...',
50900     /**
50901      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50902      */
50903     resizable: false,
50904     /**
50905      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50906      */
50907     handleHeight : 8,
50908     /**
50909      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50910      * traditional select (defaults to true)
50911      */
50912     editable: true,
50913     /**
50914      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50915      */
50916     allQuery: '',
50917     /**
50918      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50919      */
50920     mode: 'remote',
50921     /**
50922      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50923      * listWidth has a higher value)
50924      */
50925     minListWidth : 70,
50926     /**
50927      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50928      * allow the user to set arbitrary text into the field (defaults to false)
50929      */
50930     forceSelection:false,
50931     /**
50932      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50933      * if typeAhead = true (defaults to 250)
50934      */
50935     typeAheadDelay : 250,
50936     /**
50937      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50938      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50939      */
50940     valueNotFoundText : undefined,
50941     
50942     /**
50943      * @cfg {String} defaultValue The value displayed after loading the store.
50944      */
50945     defaultValue: '',
50946     
50947     /**
50948      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50949      */
50950     blockFocus : false,
50951     
50952     /**
50953      * @cfg {Boolean} disableClear Disable showing of clear button.
50954      */
50955     disableClear : false,
50956     /**
50957      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50958      */
50959     alwaysQuery : false,
50960     
50961     //private
50962     addicon : false,
50963     editicon: false,
50964     
50965     // element that contains real text value.. (when hidden is used..)
50966      
50967     // private
50968     onRender : function(ct, position){
50969         Roo.form.Field.prototype.onRender.call(this, ct, position);
50970         
50971         if(this.store){
50972             this.store.on('beforeload', this.onBeforeLoad, this);
50973             this.store.on('load', this.onLoad, this);
50974             this.store.on('loadexception', this.onLoadException, this);
50975             this.store.load({});
50976         }
50977         
50978         
50979         
50980     },
50981
50982     // private
50983     initEvents : function(){
50984         //Roo.form.ComboBox.superclass.initEvents.call(this);
50985  
50986     },
50987
50988     onDestroy : function(){
50989        
50990         if(this.store){
50991             this.store.un('beforeload', this.onBeforeLoad, this);
50992             this.store.un('load', this.onLoad, this);
50993             this.store.un('loadexception', this.onLoadException, this);
50994         }
50995         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50996     },
50997
50998     // private
50999     fireKey : function(e){
51000         if(e.isNavKeyPress() && !this.list.isVisible()){
51001             this.fireEvent("specialkey", this, e);
51002         }
51003     },
51004
51005     // private
51006     onResize: function(w, h){
51007         
51008         return; 
51009     
51010         
51011     },
51012
51013     /**
51014      * Allow or prevent the user from directly editing the field text.  If false is passed,
51015      * the user will only be able to select from the items defined in the dropdown list.  This method
51016      * is the runtime equivalent of setting the 'editable' config option at config time.
51017      * @param {Boolean} value True to allow the user to directly edit the field text
51018      */
51019     setEditable : function(value){
51020          
51021     },
51022
51023     // private
51024     onBeforeLoad : function(){
51025         
51026         Roo.log("Select before load");
51027         return;
51028     
51029         this.innerList.update(this.loadingText ?
51030                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51031         //this.restrictHeight();
51032         this.selectedIndex = -1;
51033     },
51034
51035     // private
51036     onLoad : function(){
51037
51038     
51039         var dom = this.el.dom;
51040         dom.innerHTML = '';
51041          var od = dom.ownerDocument;
51042          
51043         if (this.emptyText) {
51044             var op = od.createElement('option');
51045             op.setAttribute('value', '');
51046             op.innerHTML = String.format('{0}', this.emptyText);
51047             dom.appendChild(op);
51048         }
51049         if(this.store.getCount() > 0){
51050            
51051             var vf = this.valueField;
51052             var df = this.displayField;
51053             this.store.data.each(function(r) {
51054                 // which colmsn to use... testing - cdoe / title..
51055                 var op = od.createElement('option');
51056                 op.setAttribute('value', r.data[vf]);
51057                 op.innerHTML = String.format('{0}', r.data[df]);
51058                 dom.appendChild(op);
51059             });
51060             if (typeof(this.defaultValue != 'undefined')) {
51061                 this.setValue(this.defaultValue);
51062             }
51063             
51064              
51065         }else{
51066             //this.onEmptyResults();
51067         }
51068         //this.el.focus();
51069     },
51070     // private
51071     onLoadException : function()
51072     {
51073         dom.innerHTML = '';
51074             
51075         Roo.log("Select on load exception");
51076         return;
51077     
51078         this.collapse();
51079         Roo.log(this.store.reader.jsonData);
51080         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51081             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51082         }
51083         
51084         
51085     },
51086     // private
51087     onTypeAhead : function(){
51088          
51089     },
51090
51091     // private
51092     onSelect : function(record, index){
51093         Roo.log('on select?');
51094         return;
51095         if(this.fireEvent('beforeselect', this, record, index) !== false){
51096             this.setFromData(index > -1 ? record.data : false);
51097             this.collapse();
51098             this.fireEvent('select', this, record, index);
51099         }
51100     },
51101
51102     /**
51103      * Returns the currently selected field value or empty string if no value is set.
51104      * @return {String} value The selected value
51105      */
51106     getValue : function(){
51107         var dom = this.el.dom;
51108         this.value = dom.options[dom.selectedIndex].value;
51109         return this.value;
51110         
51111     },
51112
51113     /**
51114      * Clears any text/value currently set in the field
51115      */
51116     clearValue : function(){
51117         this.value = '';
51118         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51119         
51120     },
51121
51122     /**
51123      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51124      * will be displayed in the field.  If the value does not match the data value of an existing item,
51125      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51126      * Otherwise the field will be blank (although the value will still be set).
51127      * @param {String} value The value to match
51128      */
51129     setValue : function(v){
51130         var d = this.el.dom;
51131         for (var i =0; i < d.options.length;i++) {
51132             if (v == d.options[i].value) {
51133                 d.selectedIndex = i;
51134                 this.value = v;
51135                 return;
51136             }
51137         }
51138         this.clearValue();
51139     },
51140     /**
51141      * @property {Object} the last set data for the element
51142      */
51143     
51144     lastData : false,
51145     /**
51146      * Sets the value of the field based on a object which is related to the record format for the store.
51147      * @param {Object} value the value to set as. or false on reset?
51148      */
51149     setFromData : function(o){
51150         Roo.log('setfrom data?');
51151          
51152         
51153         
51154     },
51155     // private
51156     reset : function(){
51157         this.clearValue();
51158     },
51159     // private
51160     findRecord : function(prop, value){
51161         
51162         return false;
51163     
51164         var record;
51165         if(this.store.getCount() > 0){
51166             this.store.each(function(r){
51167                 if(r.data[prop] == value){
51168                     record = r;
51169                     return false;
51170                 }
51171                 return true;
51172             });
51173         }
51174         return record;
51175     },
51176     
51177     getName: function()
51178     {
51179         // returns hidden if it's set..
51180         if (!this.rendered) {return ''};
51181         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51182         
51183     },
51184      
51185
51186     
51187
51188     // private
51189     onEmptyResults : function(){
51190         Roo.log('empty results');
51191         //this.collapse();
51192     },
51193
51194     /**
51195      * Returns true if the dropdown list is expanded, else false.
51196      */
51197     isExpanded : function(){
51198         return false;
51199     },
51200
51201     /**
51202      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51203      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51204      * @param {String} value The data value of the item to select
51205      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51206      * selected item if it is not currently in view (defaults to true)
51207      * @return {Boolean} True if the value matched an item in the list, else false
51208      */
51209     selectByValue : function(v, scrollIntoView){
51210         Roo.log('select By Value');
51211         return false;
51212     
51213         if(v !== undefined && v !== null){
51214             var r = this.findRecord(this.valueField || this.displayField, v);
51215             if(r){
51216                 this.select(this.store.indexOf(r), scrollIntoView);
51217                 return true;
51218             }
51219         }
51220         return false;
51221     },
51222
51223     /**
51224      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51225      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51226      * @param {Number} index The zero-based index of the list item to select
51227      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51228      * selected item if it is not currently in view (defaults to true)
51229      */
51230     select : function(index, scrollIntoView){
51231         Roo.log('select ');
51232         return  ;
51233         
51234         this.selectedIndex = index;
51235         this.view.select(index);
51236         if(scrollIntoView !== false){
51237             var el = this.view.getNode(index);
51238             if(el){
51239                 this.innerList.scrollChildIntoView(el, false);
51240             }
51241         }
51242     },
51243
51244       
51245
51246     // private
51247     validateBlur : function(){
51248         
51249         return;
51250         
51251     },
51252
51253     // private
51254     initQuery : function(){
51255         this.doQuery(this.getRawValue());
51256     },
51257
51258     // private
51259     doForce : function(){
51260         if(this.el.dom.value.length > 0){
51261             this.el.dom.value =
51262                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51263              
51264         }
51265     },
51266
51267     /**
51268      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51269      * query allowing the query action to be canceled if needed.
51270      * @param {String} query The SQL query to execute
51271      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51272      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51273      * saved in the current store (defaults to false)
51274      */
51275     doQuery : function(q, forceAll){
51276         
51277         Roo.log('doQuery?');
51278         if(q === undefined || q === null){
51279             q = '';
51280         }
51281         var qe = {
51282             query: q,
51283             forceAll: forceAll,
51284             combo: this,
51285             cancel:false
51286         };
51287         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51288             return false;
51289         }
51290         q = qe.query;
51291         forceAll = qe.forceAll;
51292         if(forceAll === true || (q.length >= this.minChars)){
51293             if(this.lastQuery != q || this.alwaysQuery){
51294                 this.lastQuery = q;
51295                 if(this.mode == 'local'){
51296                     this.selectedIndex = -1;
51297                     if(forceAll){
51298                         this.store.clearFilter();
51299                     }else{
51300                         this.store.filter(this.displayField, q);
51301                     }
51302                     this.onLoad();
51303                 }else{
51304                     this.store.baseParams[this.queryParam] = q;
51305                     this.store.load({
51306                         params: this.getParams(q)
51307                     });
51308                     this.expand();
51309                 }
51310             }else{
51311                 this.selectedIndex = -1;
51312                 this.onLoad();   
51313             }
51314         }
51315     },
51316
51317     // private
51318     getParams : function(q){
51319         var p = {};
51320         //p[this.queryParam] = q;
51321         if(this.pageSize){
51322             p.start = 0;
51323             p.limit = this.pageSize;
51324         }
51325         return p;
51326     },
51327
51328     /**
51329      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51330      */
51331     collapse : function(){
51332         
51333     },
51334
51335     // private
51336     collapseIf : function(e){
51337         
51338     },
51339
51340     /**
51341      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51342      */
51343     expand : function(){
51344         
51345     } ,
51346
51347     // private
51348      
51349
51350     /** 
51351     * @cfg {Boolean} grow 
51352     * @hide 
51353     */
51354     /** 
51355     * @cfg {Number} growMin 
51356     * @hide 
51357     */
51358     /** 
51359     * @cfg {Number} growMax 
51360     * @hide 
51361     */
51362     /**
51363      * @hide
51364      * @method autoSize
51365      */
51366     
51367     setWidth : function()
51368     {
51369         
51370     },
51371     getResizeEl : function(){
51372         return this.el;
51373     }
51374 });//<script type="text/javasscript">
51375  
51376
51377 /**
51378  * @class Roo.DDView
51379  * A DnD enabled version of Roo.View.
51380  * @param {Element/String} container The Element in which to create the View.
51381  * @param {String} tpl The template string used to create the markup for each element of the View
51382  * @param {Object} config The configuration properties. These include all the config options of
51383  * {@link Roo.View} plus some specific to this class.<br>
51384  * <p>
51385  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51386  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51387  * <p>
51388  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51389 .x-view-drag-insert-above {
51390         border-top:1px dotted #3366cc;
51391 }
51392 .x-view-drag-insert-below {
51393         border-bottom:1px dotted #3366cc;
51394 }
51395 </code></pre>
51396  * 
51397  */
51398  
51399 Roo.DDView = function(container, tpl, config) {
51400     Roo.DDView.superclass.constructor.apply(this, arguments);
51401     this.getEl().setStyle("outline", "0px none");
51402     this.getEl().unselectable();
51403     if (this.dragGroup) {
51404                 this.setDraggable(this.dragGroup.split(","));
51405     }
51406     if (this.dropGroup) {
51407                 this.setDroppable(this.dropGroup.split(","));
51408     }
51409     if (this.deletable) {
51410         this.setDeletable();
51411     }
51412     this.isDirtyFlag = false;
51413         this.addEvents({
51414                 "drop" : true
51415         });
51416 };
51417
51418 Roo.extend(Roo.DDView, Roo.View, {
51419 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51420 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51421 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51422 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51423
51424         isFormField: true,
51425
51426         reset: Roo.emptyFn,
51427         
51428         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51429
51430         validate: function() {
51431                 return true;
51432         },
51433         
51434         destroy: function() {
51435                 this.purgeListeners();
51436                 this.getEl.removeAllListeners();
51437                 this.getEl().remove();
51438                 if (this.dragZone) {
51439                         if (this.dragZone.destroy) {
51440                                 this.dragZone.destroy();
51441                         }
51442                 }
51443                 if (this.dropZone) {
51444                         if (this.dropZone.destroy) {
51445                                 this.dropZone.destroy();
51446                         }
51447                 }
51448         },
51449
51450 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51451         getName: function() {
51452                 return this.name;
51453         },
51454
51455 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51456         setValue: function(v) {
51457                 if (!this.store) {
51458                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51459                 }
51460                 var data = {};
51461                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51462                 this.store.proxy = new Roo.data.MemoryProxy(data);
51463                 this.store.load();
51464         },
51465
51466 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51467         getValue: function() {
51468                 var result = '(';
51469                 this.store.each(function(rec) {
51470                         result += rec.id + ',';
51471                 });
51472                 return result.substr(0, result.length - 1) + ')';
51473         },
51474         
51475         getIds: function() {
51476                 var i = 0, result = new Array(this.store.getCount());
51477                 this.store.each(function(rec) {
51478                         result[i++] = rec.id;
51479                 });
51480                 return result;
51481         },
51482         
51483         isDirty: function() {
51484                 return this.isDirtyFlag;
51485         },
51486
51487 /**
51488  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51489  *      whole Element becomes the target, and this causes the drop gesture to append.
51490  */
51491     getTargetFromEvent : function(e) {
51492                 var target = e.getTarget();
51493                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51494                 target = target.parentNode;
51495                 }
51496                 if (!target) {
51497                         target = this.el.dom.lastChild || this.el.dom;
51498                 }
51499                 return target;
51500     },
51501
51502 /**
51503  *      Create the drag data which consists of an object which has the property "ddel" as
51504  *      the drag proxy element. 
51505  */
51506     getDragData : function(e) {
51507         var target = this.findItemFromChild(e.getTarget());
51508                 if(target) {
51509                         this.handleSelection(e);
51510                         var selNodes = this.getSelectedNodes();
51511             var dragData = {
51512                 source: this,
51513                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51514                 nodes: selNodes,
51515                 records: []
51516                         };
51517                         var selectedIndices = this.getSelectedIndexes();
51518                         for (var i = 0; i < selectedIndices.length; i++) {
51519                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51520                         }
51521                         if (selNodes.length == 1) {
51522                                 dragData.ddel = target.cloneNode(true); // the div element
51523                         } else {
51524                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51525                                 div.className = 'multi-proxy';
51526                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51527                                         div.appendChild(selNodes[i].cloneNode(true));
51528                                 }
51529                                 dragData.ddel = div;
51530                         }
51531             //console.log(dragData)
51532             //console.log(dragData.ddel.innerHTML)
51533                         return dragData;
51534                 }
51535         //console.log('nodragData')
51536                 return false;
51537     },
51538     
51539 /**     Specify to which ddGroup items in this DDView may be dragged. */
51540     setDraggable: function(ddGroup) {
51541         if (ddGroup instanceof Array) {
51542                 Roo.each(ddGroup, this.setDraggable, this);
51543                 return;
51544         }
51545         if (this.dragZone) {
51546                 this.dragZone.addToGroup(ddGroup);
51547         } else {
51548                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51549                                 containerScroll: true,
51550                                 ddGroup: ddGroup 
51551
51552                         });
51553 //                      Draggability implies selection. DragZone's mousedown selects the element.
51554                         if (!this.multiSelect) { this.singleSelect = true; }
51555
51556 //                      Wire the DragZone's handlers up to methods in *this*
51557                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51558                 }
51559     },
51560
51561 /**     Specify from which ddGroup this DDView accepts drops. */
51562     setDroppable: function(ddGroup) {
51563         if (ddGroup instanceof Array) {
51564                 Roo.each(ddGroup, this.setDroppable, this);
51565                 return;
51566         }
51567         if (this.dropZone) {
51568                 this.dropZone.addToGroup(ddGroup);
51569         } else {
51570                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51571                                 containerScroll: true,
51572                                 ddGroup: ddGroup
51573                         });
51574
51575 //                      Wire the DropZone's handlers up to methods in *this*
51576                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51577                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51578                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51579                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51580                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51581                 }
51582     },
51583
51584 /**     Decide whether to drop above or below a View node. */
51585     getDropPoint : function(e, n, dd){
51586         if (n == this.el.dom) { return "above"; }
51587                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51588                 var c = t + (b - t) / 2;
51589                 var y = Roo.lib.Event.getPageY(e);
51590                 if(y <= c) {
51591                         return "above";
51592                 }else{
51593                         return "below";
51594                 }
51595     },
51596
51597     onNodeEnter : function(n, dd, e, data){
51598                 return false;
51599     },
51600     
51601     onNodeOver : function(n, dd, e, data){
51602                 var pt = this.getDropPoint(e, n, dd);
51603                 // set the insert point style on the target node
51604                 var dragElClass = this.dropNotAllowed;
51605                 if (pt) {
51606                         var targetElClass;
51607                         if (pt == "above"){
51608                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51609                                 targetElClass = "x-view-drag-insert-above";
51610                         } else {
51611                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51612                                 targetElClass = "x-view-drag-insert-below";
51613                         }
51614                         if (this.lastInsertClass != targetElClass){
51615                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51616                                 this.lastInsertClass = targetElClass;
51617                         }
51618                 }
51619                 return dragElClass;
51620         },
51621
51622     onNodeOut : function(n, dd, e, data){
51623                 this.removeDropIndicators(n);
51624     },
51625
51626     onNodeDrop : function(n, dd, e, data){
51627         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51628                 return false;
51629         }
51630         var pt = this.getDropPoint(e, n, dd);
51631                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51632                 if (pt == "below") { insertAt++; }
51633                 for (var i = 0; i < data.records.length; i++) {
51634                         var r = data.records[i];
51635                         var dup = this.store.getById(r.id);
51636                         if (dup && (dd != this.dragZone)) {
51637                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51638                         } else {
51639                                 if (data.copy) {
51640                                         this.store.insert(insertAt++, r.copy());
51641                                 } else {
51642                                         data.source.isDirtyFlag = true;
51643                                         r.store.remove(r);
51644                                         this.store.insert(insertAt++, r);
51645                                 }
51646                                 this.isDirtyFlag = true;
51647                         }
51648                 }
51649                 this.dragZone.cachedTarget = null;
51650                 return true;
51651     },
51652
51653     removeDropIndicators : function(n){
51654                 if(n){
51655                         Roo.fly(n).removeClass([
51656                                 "x-view-drag-insert-above",
51657                                 "x-view-drag-insert-below"]);
51658                         this.lastInsertClass = "_noclass";
51659                 }
51660     },
51661
51662 /**
51663  *      Utility method. Add a delete option to the DDView's context menu.
51664  *      @param {String} imageUrl The URL of the "delete" icon image.
51665  */
51666         setDeletable: function(imageUrl) {
51667                 if (!this.singleSelect && !this.multiSelect) {
51668                         this.singleSelect = true;
51669                 }
51670                 var c = this.getContextMenu();
51671                 this.contextMenu.on("itemclick", function(item) {
51672                         switch (item.id) {
51673                                 case "delete":
51674                                         this.remove(this.getSelectedIndexes());
51675                                         break;
51676                         }
51677                 }, this);
51678                 this.contextMenu.add({
51679                         icon: imageUrl,
51680                         id: "delete",
51681                         text: 'Delete'
51682                 });
51683         },
51684         
51685 /**     Return the context menu for this DDView. */
51686         getContextMenu: function() {
51687                 if (!this.contextMenu) {
51688 //                      Create the View's context menu
51689                         this.contextMenu = new Roo.menu.Menu({
51690                                 id: this.id + "-contextmenu"
51691                         });
51692                         this.el.on("contextmenu", this.showContextMenu, this);
51693                 }
51694                 return this.contextMenu;
51695         },
51696         
51697         disableContextMenu: function() {
51698                 if (this.contextMenu) {
51699                         this.el.un("contextmenu", this.showContextMenu, this);
51700                 }
51701         },
51702
51703         showContextMenu: function(e, item) {
51704         item = this.findItemFromChild(e.getTarget());
51705                 if (item) {
51706                         e.stopEvent();
51707                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51708                         this.contextMenu.showAt(e.getXY());
51709             }
51710     },
51711
51712 /**
51713  *      Remove {@link Roo.data.Record}s at the specified indices.
51714  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51715  */
51716     remove: function(selectedIndices) {
51717                 selectedIndices = [].concat(selectedIndices);
51718                 for (var i = 0; i < selectedIndices.length; i++) {
51719                         var rec = this.store.getAt(selectedIndices[i]);
51720                         this.store.remove(rec);
51721                 }
51722     },
51723
51724 /**
51725  *      Double click fires the event, but also, if this is draggable, and there is only one other
51726  *      related DropZone, it transfers the selected node.
51727  */
51728     onDblClick : function(e){
51729         var item = this.findItemFromChild(e.getTarget());
51730         if(item){
51731             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51732                 return false;
51733             }
51734             if (this.dragGroup) {
51735                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51736                     while (targets.indexOf(this.dropZone) > -1) {
51737                             targets.remove(this.dropZone);
51738                                 }
51739                     if (targets.length == 1) {
51740                                         this.dragZone.cachedTarget = null;
51741                         var el = Roo.get(targets[0].getEl());
51742                         var box = el.getBox(true);
51743                         targets[0].onNodeDrop(el.dom, {
51744                                 target: el.dom,
51745                                 xy: [box.x, box.y + box.height - 1]
51746                         }, null, this.getDragData(e));
51747                     }
51748                 }
51749         }
51750     },
51751     
51752     handleSelection: function(e) {
51753                 this.dragZone.cachedTarget = null;
51754         var item = this.findItemFromChild(e.getTarget());
51755         if (!item) {
51756                 this.clearSelections(true);
51757                 return;
51758         }
51759                 if (item && (this.multiSelect || this.singleSelect)){
51760                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51761                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51762                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51763                                 this.unselect(item);
51764                         } else {
51765                                 this.select(item, this.multiSelect && e.ctrlKey);
51766                                 this.lastSelection = item;
51767                         }
51768                 }
51769     },
51770
51771     onItemClick : function(item, index, e){
51772                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51773                         return false;
51774                 }
51775                 return true;
51776     },
51777
51778     unselect : function(nodeInfo, suppressEvent){
51779                 var node = this.getNode(nodeInfo);
51780                 if(node && this.isSelected(node)){
51781                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51782                                 Roo.fly(node).removeClass(this.selectedClass);
51783                                 this.selections.remove(node);
51784                                 if(!suppressEvent){
51785                                         this.fireEvent("selectionchange", this, this.selections);
51786                                 }
51787                         }
51788                 }
51789     }
51790 });
51791 /*
51792  * Based on:
51793  * Ext JS Library 1.1.1
51794  * Copyright(c) 2006-2007, Ext JS, LLC.
51795  *
51796  * Originally Released Under LGPL - original licence link has changed is not relivant.
51797  *
51798  * Fork - LGPL
51799  * <script type="text/javascript">
51800  */
51801  
51802 /**
51803  * @class Roo.LayoutManager
51804  * @extends Roo.util.Observable
51805  * Base class for layout managers.
51806  */
51807 Roo.LayoutManager = function(container, config){
51808     Roo.LayoutManager.superclass.constructor.call(this);
51809     this.el = Roo.get(container);
51810     // ie scrollbar fix
51811     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51812         document.body.scroll = "no";
51813     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51814         this.el.position('relative');
51815     }
51816     this.id = this.el.id;
51817     this.el.addClass("x-layout-container");
51818     /** false to disable window resize monitoring @type Boolean */
51819     this.monitorWindowResize = true;
51820     this.regions = {};
51821     this.addEvents({
51822         /**
51823          * @event layout
51824          * Fires when a layout is performed. 
51825          * @param {Roo.LayoutManager} this
51826          */
51827         "layout" : true,
51828         /**
51829          * @event regionresized
51830          * Fires when the user resizes a region. 
51831          * @param {Roo.LayoutRegion} region The resized region
51832          * @param {Number} newSize The new size (width for east/west, height for north/south)
51833          */
51834         "regionresized" : true,
51835         /**
51836          * @event regioncollapsed
51837          * Fires when a region is collapsed. 
51838          * @param {Roo.LayoutRegion} region The collapsed region
51839          */
51840         "regioncollapsed" : true,
51841         /**
51842          * @event regionexpanded
51843          * Fires when a region is expanded.  
51844          * @param {Roo.LayoutRegion} region The expanded region
51845          */
51846         "regionexpanded" : true
51847     });
51848     this.updating = false;
51849     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51850 };
51851
51852 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51853     /**
51854      * Returns true if this layout is currently being updated
51855      * @return {Boolean}
51856      */
51857     isUpdating : function(){
51858         return this.updating; 
51859     },
51860     
51861     /**
51862      * Suspend the LayoutManager from doing auto-layouts while
51863      * making multiple add or remove calls
51864      */
51865     beginUpdate : function(){
51866         this.updating = true;    
51867     },
51868     
51869     /**
51870      * Restore auto-layouts and optionally disable the manager from performing a layout
51871      * @param {Boolean} noLayout true to disable a layout update 
51872      */
51873     endUpdate : function(noLayout){
51874         this.updating = false;
51875         if(!noLayout){
51876             this.layout();
51877         }    
51878     },
51879     
51880     layout: function(){
51881         
51882     },
51883     
51884     onRegionResized : function(region, newSize){
51885         this.fireEvent("regionresized", region, newSize);
51886         this.layout();
51887     },
51888     
51889     onRegionCollapsed : function(region){
51890         this.fireEvent("regioncollapsed", region);
51891     },
51892     
51893     onRegionExpanded : function(region){
51894         this.fireEvent("regionexpanded", region);
51895     },
51896         
51897     /**
51898      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51899      * performs box-model adjustments.
51900      * @return {Object} The size as an object {width: (the width), height: (the height)}
51901      */
51902     getViewSize : function(){
51903         var size;
51904         if(this.el.dom != document.body){
51905             size = this.el.getSize();
51906         }else{
51907             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51908         }
51909         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51910         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51911         return size;
51912     },
51913     
51914     /**
51915      * Returns the Element this layout is bound to.
51916      * @return {Roo.Element}
51917      */
51918     getEl : function(){
51919         return this.el;
51920     },
51921     
51922     /**
51923      * Returns the specified region.
51924      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51925      * @return {Roo.LayoutRegion}
51926      */
51927     getRegion : function(target){
51928         return this.regions[target.toLowerCase()];
51929     },
51930     
51931     onWindowResize : function(){
51932         if(this.monitorWindowResize){
51933             this.layout();
51934         }
51935     }
51936 });/*
51937  * Based on:
51938  * Ext JS Library 1.1.1
51939  * Copyright(c) 2006-2007, Ext JS, LLC.
51940  *
51941  * Originally Released Under LGPL - original licence link has changed is not relivant.
51942  *
51943  * Fork - LGPL
51944  * <script type="text/javascript">
51945  */
51946 /**
51947  * @class Roo.BorderLayout
51948  * @extends Roo.LayoutManager
51949  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51950  * please see: <br><br>
51951  * <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>
51952  * <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>
51953  * Example:
51954  <pre><code>
51955  var layout = new Roo.BorderLayout(document.body, {
51956     north: {
51957         initialSize: 25,
51958         titlebar: false
51959     },
51960     west: {
51961         split:true,
51962         initialSize: 200,
51963         minSize: 175,
51964         maxSize: 400,
51965         titlebar: true,
51966         collapsible: true
51967     },
51968     east: {
51969         split:true,
51970         initialSize: 202,
51971         minSize: 175,
51972         maxSize: 400,
51973         titlebar: true,
51974         collapsible: true
51975     },
51976     south: {
51977         split:true,
51978         initialSize: 100,
51979         minSize: 100,
51980         maxSize: 200,
51981         titlebar: true,
51982         collapsible: true
51983     },
51984     center: {
51985         titlebar: true,
51986         autoScroll:true,
51987         resizeTabs: true,
51988         minTabWidth: 50,
51989         preferredTabWidth: 150
51990     }
51991 });
51992
51993 // shorthand
51994 var CP = Roo.ContentPanel;
51995
51996 layout.beginUpdate();
51997 layout.add("north", new CP("north", "North"));
51998 layout.add("south", new CP("south", {title: "South", closable: true}));
51999 layout.add("west", new CP("west", {title: "West"}));
52000 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52001 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52002 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52003 layout.getRegion("center").showPanel("center1");
52004 layout.endUpdate();
52005 </code></pre>
52006
52007 <b>The container the layout is rendered into can be either the body element or any other element.
52008 If it is not the body element, the container needs to either be an absolute positioned element,
52009 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52010 the container size if it is not the body element.</b>
52011
52012 * @constructor
52013 * Create a new BorderLayout
52014 * @param {String/HTMLElement/Element} container The container this layout is bound to
52015 * @param {Object} config Configuration options
52016  */
52017 Roo.BorderLayout = function(container, config){
52018     config = config || {};
52019     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52020     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52021     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52022         var target = this.factory.validRegions[i];
52023         if(config[target]){
52024             this.addRegion(target, config[target]);
52025         }
52026     }
52027 };
52028
52029 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52030     /**
52031      * Creates and adds a new region if it doesn't already exist.
52032      * @param {String} target The target region key (north, south, east, west or center).
52033      * @param {Object} config The regions config object
52034      * @return {BorderLayoutRegion} The new region
52035      */
52036     addRegion : function(target, config){
52037         if(!this.regions[target]){
52038             var r = this.factory.create(target, this, config);
52039             this.bindRegion(target, r);
52040         }
52041         return this.regions[target];
52042     },
52043
52044     // private (kinda)
52045     bindRegion : function(name, r){
52046         this.regions[name] = r;
52047         r.on("visibilitychange", this.layout, this);
52048         r.on("paneladded", this.layout, this);
52049         r.on("panelremoved", this.layout, this);
52050         r.on("invalidated", this.layout, this);
52051         r.on("resized", this.onRegionResized, this);
52052         r.on("collapsed", this.onRegionCollapsed, this);
52053         r.on("expanded", this.onRegionExpanded, this);
52054     },
52055
52056     /**
52057      * Performs a layout update.
52058      */
52059     layout : function(){
52060         if(this.updating) {
52061             return;
52062         }
52063         var size = this.getViewSize();
52064         var w = size.width;
52065         var h = size.height;
52066         var centerW = w;
52067         var centerH = h;
52068         var centerY = 0;
52069         var centerX = 0;
52070         //var x = 0, y = 0;
52071
52072         var rs = this.regions;
52073         var north = rs["north"];
52074         var south = rs["south"]; 
52075         var west = rs["west"];
52076         var east = rs["east"];
52077         var center = rs["center"];
52078         //if(this.hideOnLayout){ // not supported anymore
52079             //c.el.setStyle("display", "none");
52080         //}
52081         if(north && north.isVisible()){
52082             var b = north.getBox();
52083             var m = north.getMargins();
52084             b.width = w - (m.left+m.right);
52085             b.x = m.left;
52086             b.y = m.top;
52087             centerY = b.height + b.y + m.bottom;
52088             centerH -= centerY;
52089             north.updateBox(this.safeBox(b));
52090         }
52091         if(south && south.isVisible()){
52092             var b = south.getBox();
52093             var m = south.getMargins();
52094             b.width = w - (m.left+m.right);
52095             b.x = m.left;
52096             var totalHeight = (b.height + m.top + m.bottom);
52097             b.y = h - totalHeight + m.top;
52098             centerH -= totalHeight;
52099             south.updateBox(this.safeBox(b));
52100         }
52101         if(west && west.isVisible()){
52102             var b = west.getBox();
52103             var m = west.getMargins();
52104             b.height = centerH - (m.top+m.bottom);
52105             b.x = m.left;
52106             b.y = centerY + m.top;
52107             var totalWidth = (b.width + m.left + m.right);
52108             centerX += totalWidth;
52109             centerW -= totalWidth;
52110             west.updateBox(this.safeBox(b));
52111         }
52112         if(east && east.isVisible()){
52113             var b = east.getBox();
52114             var m = east.getMargins();
52115             b.height = centerH - (m.top+m.bottom);
52116             var totalWidth = (b.width + m.left + m.right);
52117             b.x = w - totalWidth + m.left;
52118             b.y = centerY + m.top;
52119             centerW -= totalWidth;
52120             east.updateBox(this.safeBox(b));
52121         }
52122         if(center){
52123             var m = center.getMargins();
52124             var centerBox = {
52125                 x: centerX + m.left,
52126                 y: centerY + m.top,
52127                 width: centerW - (m.left+m.right),
52128                 height: centerH - (m.top+m.bottom)
52129             };
52130             //if(this.hideOnLayout){
52131                 //center.el.setStyle("display", "block");
52132             //}
52133             center.updateBox(this.safeBox(centerBox));
52134         }
52135         this.el.repaint();
52136         this.fireEvent("layout", this);
52137     },
52138
52139     // private
52140     safeBox : function(box){
52141         box.width = Math.max(0, box.width);
52142         box.height = Math.max(0, box.height);
52143         return box;
52144     },
52145
52146     /**
52147      * Adds a ContentPanel (or subclass) to this layout.
52148      * @param {String} target The target region key (north, south, east, west or center).
52149      * @param {Roo.ContentPanel} panel The panel to add
52150      * @return {Roo.ContentPanel} The added panel
52151      */
52152     add : function(target, panel){
52153          
52154         target = target.toLowerCase();
52155         return this.regions[target].add(panel);
52156     },
52157
52158     /**
52159      * Remove a ContentPanel (or subclass) to this layout.
52160      * @param {String} target The target region key (north, south, east, west or center).
52161      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52162      * @return {Roo.ContentPanel} The removed panel
52163      */
52164     remove : function(target, panel){
52165         target = target.toLowerCase();
52166         return this.regions[target].remove(panel);
52167     },
52168
52169     /**
52170      * Searches all regions for a panel with the specified id
52171      * @param {String} panelId
52172      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52173      */
52174     findPanel : function(panelId){
52175         var rs = this.regions;
52176         for(var target in rs){
52177             if(typeof rs[target] != "function"){
52178                 var p = rs[target].getPanel(panelId);
52179                 if(p){
52180                     return p;
52181                 }
52182             }
52183         }
52184         return null;
52185     },
52186
52187     /**
52188      * Searches all regions for a panel with the specified id and activates (shows) it.
52189      * @param {String/ContentPanel} panelId The panels id or the panel itself
52190      * @return {Roo.ContentPanel} The shown panel or null
52191      */
52192     showPanel : function(panelId) {
52193       var rs = this.regions;
52194       for(var target in rs){
52195          var r = rs[target];
52196          if(typeof r != "function"){
52197             if(r.hasPanel(panelId)){
52198                return r.showPanel(panelId);
52199             }
52200          }
52201       }
52202       return null;
52203    },
52204
52205    /**
52206      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52207      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52208      */
52209     restoreState : function(provider){
52210         if(!provider){
52211             provider = Roo.state.Manager;
52212         }
52213         var sm = new Roo.LayoutStateManager();
52214         sm.init(this, provider);
52215     },
52216
52217     /**
52218      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52219      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52220      * a valid ContentPanel config object.  Example:
52221      * <pre><code>
52222 // Create the main layout
52223 var layout = new Roo.BorderLayout('main-ct', {
52224     west: {
52225         split:true,
52226         minSize: 175,
52227         titlebar: true
52228     },
52229     center: {
52230         title:'Components'
52231     }
52232 }, 'main-ct');
52233
52234 // Create and add multiple ContentPanels at once via configs
52235 layout.batchAdd({
52236    west: {
52237        id: 'source-files',
52238        autoCreate:true,
52239        title:'Ext Source Files',
52240        autoScroll:true,
52241        fitToFrame:true
52242    },
52243    center : {
52244        el: cview,
52245        autoScroll:true,
52246        fitToFrame:true,
52247        toolbar: tb,
52248        resizeEl:'cbody'
52249    }
52250 });
52251 </code></pre>
52252      * @param {Object} regions An object containing ContentPanel configs by region name
52253      */
52254     batchAdd : function(regions){
52255         this.beginUpdate();
52256         for(var rname in regions){
52257             var lr = this.regions[rname];
52258             if(lr){
52259                 this.addTypedPanels(lr, regions[rname]);
52260             }
52261         }
52262         this.endUpdate();
52263     },
52264
52265     // private
52266     addTypedPanels : function(lr, ps){
52267         if(typeof ps == 'string'){
52268             lr.add(new Roo.ContentPanel(ps));
52269         }
52270         else if(ps instanceof Array){
52271             for(var i =0, len = ps.length; i < len; i++){
52272                 this.addTypedPanels(lr, ps[i]);
52273             }
52274         }
52275         else if(!ps.events){ // raw config?
52276             var el = ps.el;
52277             delete ps.el; // prevent conflict
52278             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52279         }
52280         else {  // panel object assumed!
52281             lr.add(ps);
52282         }
52283     },
52284     /**
52285      * Adds a xtype elements to the layout.
52286      * <pre><code>
52287
52288 layout.addxtype({
52289        xtype : 'ContentPanel',
52290        region: 'west',
52291        items: [ .... ]
52292    }
52293 );
52294
52295 layout.addxtype({
52296         xtype : 'NestedLayoutPanel',
52297         region: 'west',
52298         layout: {
52299            center: { },
52300            west: { }   
52301         },
52302         items : [ ... list of content panels or nested layout panels.. ]
52303    }
52304 );
52305 </code></pre>
52306      * @param {Object} cfg Xtype definition of item to add.
52307      */
52308     addxtype : function(cfg)
52309     {
52310         // basically accepts a pannel...
52311         // can accept a layout region..!?!?
52312         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52313         
52314         if (!cfg.xtype.match(/Panel$/)) {
52315             return false;
52316         }
52317         var ret = false;
52318         
52319         if (typeof(cfg.region) == 'undefined') {
52320             Roo.log("Failed to add Panel, region was not set");
52321             Roo.log(cfg);
52322             return false;
52323         }
52324         var region = cfg.region;
52325         delete cfg.region;
52326         
52327           
52328         var xitems = [];
52329         if (cfg.items) {
52330             xitems = cfg.items;
52331             delete cfg.items;
52332         }
52333         var nb = false;
52334         
52335         switch(cfg.xtype) 
52336         {
52337             case 'ContentPanel':  // ContentPanel (el, cfg)
52338             case 'ScrollPanel':  // ContentPanel (el, cfg)
52339             case 'ViewPanel': 
52340                 if(cfg.autoCreate) {
52341                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52342                 } else {
52343                     var el = this.el.createChild();
52344                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52345                 }
52346                 
52347                 this.add(region, ret);
52348                 break;
52349             
52350             
52351             case 'TreePanel': // our new panel!
52352                 cfg.el = this.el.createChild();
52353                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52354                 this.add(region, ret);
52355                 break;
52356             
52357             case 'NestedLayoutPanel': 
52358                 // create a new Layout (which is  a Border Layout...
52359                 var el = this.el.createChild();
52360                 var clayout = cfg.layout;
52361                 delete cfg.layout;
52362                 clayout.items   = clayout.items  || [];
52363                 // replace this exitems with the clayout ones..
52364                 xitems = clayout.items;
52365                  
52366                 
52367                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52368                     cfg.background = false;
52369                 }
52370                 var layout = new Roo.BorderLayout(el, clayout);
52371                 
52372                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52373                 //console.log('adding nested layout panel '  + cfg.toSource());
52374                 this.add(region, ret);
52375                 nb = {}; /// find first...
52376                 break;
52377                 
52378             case 'GridPanel': 
52379             
52380                 // needs grid and region
52381                 
52382                 //var el = this.getRegion(region).el.createChild();
52383                 var el = this.el.createChild();
52384                 // create the grid first...
52385                 
52386                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52387                 delete cfg.grid;
52388                 if (region == 'center' && this.active ) {
52389                     cfg.background = false;
52390                 }
52391                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52392                 
52393                 this.add(region, ret);
52394                 if (cfg.background) {
52395                     ret.on('activate', function(gp) {
52396                         if (!gp.grid.rendered) {
52397                             gp.grid.render();
52398                         }
52399                     });
52400                 } else {
52401                     grid.render();
52402                 }
52403                 break;
52404            
52405            
52406            
52407                 
52408                 
52409                 
52410             default:
52411                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52412                     
52413                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52414                     this.add(region, ret);
52415                 } else {
52416                 
52417                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52418                     return null;
52419                 }
52420                 
52421              // GridPanel (grid, cfg)
52422             
52423         }
52424         this.beginUpdate();
52425         // add children..
52426         var region = '';
52427         var abn = {};
52428         Roo.each(xitems, function(i)  {
52429             region = nb && i.region ? i.region : false;
52430             
52431             var add = ret.addxtype(i);
52432            
52433             if (region) {
52434                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52435                 if (!i.background) {
52436                     abn[region] = nb[region] ;
52437                 }
52438             }
52439             
52440         });
52441         this.endUpdate();
52442
52443         // make the last non-background panel active..
52444         //if (nb) { Roo.log(abn); }
52445         if (nb) {
52446             
52447             for(var r in abn) {
52448                 region = this.getRegion(r);
52449                 if (region) {
52450                     // tried using nb[r], but it does not work..
52451                      
52452                     region.showPanel(abn[r]);
52453                    
52454                 }
52455             }
52456         }
52457         return ret;
52458         
52459     }
52460 });
52461
52462 /**
52463  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52464  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52465  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52466  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52467  * <pre><code>
52468 // shorthand
52469 var CP = Roo.ContentPanel;
52470
52471 var layout = Roo.BorderLayout.create({
52472     north: {
52473         initialSize: 25,
52474         titlebar: false,
52475         panels: [new CP("north", "North")]
52476     },
52477     west: {
52478         split:true,
52479         initialSize: 200,
52480         minSize: 175,
52481         maxSize: 400,
52482         titlebar: true,
52483         collapsible: true,
52484         panels: [new CP("west", {title: "West"})]
52485     },
52486     east: {
52487         split:true,
52488         initialSize: 202,
52489         minSize: 175,
52490         maxSize: 400,
52491         titlebar: true,
52492         collapsible: true,
52493         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52494     },
52495     south: {
52496         split:true,
52497         initialSize: 100,
52498         minSize: 100,
52499         maxSize: 200,
52500         titlebar: true,
52501         collapsible: true,
52502         panels: [new CP("south", {title: "South", closable: true})]
52503     },
52504     center: {
52505         titlebar: true,
52506         autoScroll:true,
52507         resizeTabs: true,
52508         minTabWidth: 50,
52509         preferredTabWidth: 150,
52510         panels: [
52511             new CP("center1", {title: "Close Me", closable: true}),
52512             new CP("center2", {title: "Center Panel", closable: false})
52513         ]
52514     }
52515 }, document.body);
52516
52517 layout.getRegion("center").showPanel("center1");
52518 </code></pre>
52519  * @param config
52520  * @param targetEl
52521  */
52522 Roo.BorderLayout.create = function(config, targetEl){
52523     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52524     layout.beginUpdate();
52525     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52526     for(var j = 0, jlen = regions.length; j < jlen; j++){
52527         var lr = regions[j];
52528         if(layout.regions[lr] && config[lr].panels){
52529             var r = layout.regions[lr];
52530             var ps = config[lr].panels;
52531             layout.addTypedPanels(r, ps);
52532         }
52533     }
52534     layout.endUpdate();
52535     return layout;
52536 };
52537
52538 // private
52539 Roo.BorderLayout.RegionFactory = {
52540     // private
52541     validRegions : ["north","south","east","west","center"],
52542
52543     // private
52544     create : function(target, mgr, config){
52545         target = target.toLowerCase();
52546         if(config.lightweight || config.basic){
52547             return new Roo.BasicLayoutRegion(mgr, config, target);
52548         }
52549         switch(target){
52550             case "north":
52551                 return new Roo.NorthLayoutRegion(mgr, config);
52552             case "south":
52553                 return new Roo.SouthLayoutRegion(mgr, config);
52554             case "east":
52555                 return new Roo.EastLayoutRegion(mgr, config);
52556             case "west":
52557                 return new Roo.WestLayoutRegion(mgr, config);
52558             case "center":
52559                 return new Roo.CenterLayoutRegion(mgr, config);
52560         }
52561         throw 'Layout region "'+target+'" not supported.';
52562     }
52563 };/*
52564  * Based on:
52565  * Ext JS Library 1.1.1
52566  * Copyright(c) 2006-2007, Ext JS, LLC.
52567  *
52568  * Originally Released Under LGPL - original licence link has changed is not relivant.
52569  *
52570  * Fork - LGPL
52571  * <script type="text/javascript">
52572  */
52573  
52574 /**
52575  * @class Roo.BasicLayoutRegion
52576  * @extends Roo.util.Observable
52577  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52578  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52579  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52580  */
52581 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52582     this.mgr = mgr;
52583     this.position  = pos;
52584     this.events = {
52585         /**
52586          * @scope Roo.BasicLayoutRegion
52587          */
52588         
52589         /**
52590          * @event beforeremove
52591          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52592          * @param {Roo.LayoutRegion} this
52593          * @param {Roo.ContentPanel} panel The panel
52594          * @param {Object} e The cancel event object
52595          */
52596         "beforeremove" : true,
52597         /**
52598          * @event invalidated
52599          * Fires when the layout for this region is changed.
52600          * @param {Roo.LayoutRegion} this
52601          */
52602         "invalidated" : true,
52603         /**
52604          * @event visibilitychange
52605          * Fires when this region is shown or hidden 
52606          * @param {Roo.LayoutRegion} this
52607          * @param {Boolean} visibility true or false
52608          */
52609         "visibilitychange" : true,
52610         /**
52611          * @event paneladded
52612          * Fires when a panel is added. 
52613          * @param {Roo.LayoutRegion} this
52614          * @param {Roo.ContentPanel} panel The panel
52615          */
52616         "paneladded" : true,
52617         /**
52618          * @event panelremoved
52619          * Fires when a panel is removed. 
52620          * @param {Roo.LayoutRegion} this
52621          * @param {Roo.ContentPanel} panel The panel
52622          */
52623         "panelremoved" : true,
52624         /**
52625          * @event beforecollapse
52626          * Fires when this region before collapse.
52627          * @param {Roo.LayoutRegion} this
52628          */
52629         "beforecollapse" : true,
52630         /**
52631          * @event collapsed
52632          * Fires when this region is collapsed.
52633          * @param {Roo.LayoutRegion} this
52634          */
52635         "collapsed" : true,
52636         /**
52637          * @event expanded
52638          * Fires when this region is expanded.
52639          * @param {Roo.LayoutRegion} this
52640          */
52641         "expanded" : true,
52642         /**
52643          * @event slideshow
52644          * Fires when this region is slid into view.
52645          * @param {Roo.LayoutRegion} this
52646          */
52647         "slideshow" : true,
52648         /**
52649          * @event slidehide
52650          * Fires when this region slides out of view. 
52651          * @param {Roo.LayoutRegion} this
52652          */
52653         "slidehide" : true,
52654         /**
52655          * @event panelactivated
52656          * Fires when a panel is activated. 
52657          * @param {Roo.LayoutRegion} this
52658          * @param {Roo.ContentPanel} panel The activated panel
52659          */
52660         "panelactivated" : true,
52661         /**
52662          * @event resized
52663          * Fires when the user resizes this region. 
52664          * @param {Roo.LayoutRegion} this
52665          * @param {Number} newSize The new size (width for east/west, height for north/south)
52666          */
52667         "resized" : true
52668     };
52669     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52670     this.panels = new Roo.util.MixedCollection();
52671     this.panels.getKey = this.getPanelId.createDelegate(this);
52672     this.box = null;
52673     this.activePanel = null;
52674     // ensure listeners are added...
52675     
52676     if (config.listeners || config.events) {
52677         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52678             listeners : config.listeners || {},
52679             events : config.events || {}
52680         });
52681     }
52682     
52683     if(skipConfig !== true){
52684         this.applyConfig(config);
52685     }
52686 };
52687
52688 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52689     getPanelId : function(p){
52690         return p.getId();
52691     },
52692     
52693     applyConfig : function(config){
52694         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52695         this.config = config;
52696         
52697     },
52698     
52699     /**
52700      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52701      * the width, for horizontal (north, south) the height.
52702      * @param {Number} newSize The new width or height
52703      */
52704     resizeTo : function(newSize){
52705         var el = this.el ? this.el :
52706                  (this.activePanel ? this.activePanel.getEl() : null);
52707         if(el){
52708             switch(this.position){
52709                 case "east":
52710                 case "west":
52711                     el.setWidth(newSize);
52712                     this.fireEvent("resized", this, newSize);
52713                 break;
52714                 case "north":
52715                 case "south":
52716                     el.setHeight(newSize);
52717                     this.fireEvent("resized", this, newSize);
52718                 break;                
52719             }
52720         }
52721     },
52722     
52723     getBox : function(){
52724         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52725     },
52726     
52727     getMargins : function(){
52728         return this.margins;
52729     },
52730     
52731     updateBox : function(box){
52732         this.box = box;
52733         var el = this.activePanel.getEl();
52734         el.dom.style.left = box.x + "px";
52735         el.dom.style.top = box.y + "px";
52736         this.activePanel.setSize(box.width, box.height);
52737     },
52738     
52739     /**
52740      * Returns the container element for this region.
52741      * @return {Roo.Element}
52742      */
52743     getEl : function(){
52744         return this.activePanel;
52745     },
52746     
52747     /**
52748      * Returns true if this region is currently visible.
52749      * @return {Boolean}
52750      */
52751     isVisible : function(){
52752         return this.activePanel ? true : false;
52753     },
52754     
52755     setActivePanel : function(panel){
52756         panel = this.getPanel(panel);
52757         if(this.activePanel && this.activePanel != panel){
52758             this.activePanel.setActiveState(false);
52759             this.activePanel.getEl().setLeftTop(-10000,-10000);
52760         }
52761         this.activePanel = panel;
52762         panel.setActiveState(true);
52763         if(this.box){
52764             panel.setSize(this.box.width, this.box.height);
52765         }
52766         this.fireEvent("panelactivated", this, panel);
52767         this.fireEvent("invalidated");
52768     },
52769     
52770     /**
52771      * Show the specified panel.
52772      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52773      * @return {Roo.ContentPanel} The shown panel or null
52774      */
52775     showPanel : function(panel){
52776         if(panel = this.getPanel(panel)){
52777             this.setActivePanel(panel);
52778         }
52779         return panel;
52780     },
52781     
52782     /**
52783      * Get the active panel for this region.
52784      * @return {Roo.ContentPanel} The active panel or null
52785      */
52786     getActivePanel : function(){
52787         return this.activePanel;
52788     },
52789     
52790     /**
52791      * Add the passed ContentPanel(s)
52792      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52793      * @return {Roo.ContentPanel} The panel added (if only one was added)
52794      */
52795     add : function(panel){
52796         if(arguments.length > 1){
52797             for(var i = 0, len = arguments.length; i < len; i++) {
52798                 this.add(arguments[i]);
52799             }
52800             return null;
52801         }
52802         if(this.hasPanel(panel)){
52803             this.showPanel(panel);
52804             return panel;
52805         }
52806         var el = panel.getEl();
52807         if(el.dom.parentNode != this.mgr.el.dom){
52808             this.mgr.el.dom.appendChild(el.dom);
52809         }
52810         if(panel.setRegion){
52811             panel.setRegion(this);
52812         }
52813         this.panels.add(panel);
52814         el.setStyle("position", "absolute");
52815         if(!panel.background){
52816             this.setActivePanel(panel);
52817             if(this.config.initialSize && this.panels.getCount()==1){
52818                 this.resizeTo(this.config.initialSize);
52819             }
52820         }
52821         this.fireEvent("paneladded", this, panel);
52822         return panel;
52823     },
52824     
52825     /**
52826      * Returns true if the panel is in this region.
52827      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52828      * @return {Boolean}
52829      */
52830     hasPanel : function(panel){
52831         if(typeof panel == "object"){ // must be panel obj
52832             panel = panel.getId();
52833         }
52834         return this.getPanel(panel) ? true : false;
52835     },
52836     
52837     /**
52838      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52839      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52840      * @param {Boolean} preservePanel Overrides the config preservePanel option
52841      * @return {Roo.ContentPanel} The panel that was removed
52842      */
52843     remove : function(panel, preservePanel){
52844         panel = this.getPanel(panel);
52845         if(!panel){
52846             return null;
52847         }
52848         var e = {};
52849         this.fireEvent("beforeremove", this, panel, e);
52850         if(e.cancel === true){
52851             return null;
52852         }
52853         var panelId = panel.getId();
52854         this.panels.removeKey(panelId);
52855         return panel;
52856     },
52857     
52858     /**
52859      * Returns the panel specified or null if it's not in this region.
52860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52861      * @return {Roo.ContentPanel}
52862      */
52863     getPanel : function(id){
52864         if(typeof id == "object"){ // must be panel obj
52865             return id;
52866         }
52867         return this.panels.get(id);
52868     },
52869     
52870     /**
52871      * Returns this regions position (north/south/east/west/center).
52872      * @return {String} 
52873      */
52874     getPosition: function(){
52875         return this.position;    
52876     }
52877 });/*
52878  * Based on:
52879  * Ext JS Library 1.1.1
52880  * Copyright(c) 2006-2007, Ext JS, LLC.
52881  *
52882  * Originally Released Under LGPL - original licence link has changed is not relivant.
52883  *
52884  * Fork - LGPL
52885  * <script type="text/javascript">
52886  */
52887  
52888 /**
52889  * @class Roo.LayoutRegion
52890  * @extends Roo.BasicLayoutRegion
52891  * This class represents a region in a layout manager.
52892  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52893  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52894  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52895  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52896  * @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})
52897  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52898  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52899  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52900  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52901  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52902  * @cfg {String}    title           The title for the region (overrides panel titles)
52903  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52904  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52905  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52906  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52907  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52908  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52909  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52910  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52911  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52912  * @cfg {Boolean}   showPin         True to show a pin button
52913  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52914  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52915  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52916  * @cfg {Number}    width           For East/West panels
52917  * @cfg {Number}    height          For North/South panels
52918  * @cfg {Boolean}   split           To show the splitter
52919  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52920  */
52921 Roo.LayoutRegion = function(mgr, config, pos){
52922     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52923     var dh = Roo.DomHelper;
52924     /** This region's container element 
52925     * @type Roo.Element */
52926     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52927     /** This region's title element 
52928     * @type Roo.Element */
52929
52930     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52931         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52932         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52933     ]}, true);
52934     this.titleEl.enableDisplayMode();
52935     /** This region's title text element 
52936     * @type HTMLElement */
52937     this.titleTextEl = this.titleEl.dom.firstChild;
52938     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52939     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52940     this.closeBtn.enableDisplayMode();
52941     this.closeBtn.on("click", this.closeClicked, this);
52942     this.closeBtn.hide();
52943
52944     this.createBody(config);
52945     this.visible = true;
52946     this.collapsed = false;
52947
52948     if(config.hideWhenEmpty){
52949         this.hide();
52950         this.on("paneladded", this.validateVisibility, this);
52951         this.on("panelremoved", this.validateVisibility, this);
52952     }
52953     this.applyConfig(config);
52954 };
52955
52956 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52957
52958     createBody : function(){
52959         /** This region's body element 
52960         * @type Roo.Element */
52961         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52962     },
52963
52964     applyConfig : function(c){
52965         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52966             var dh = Roo.DomHelper;
52967             if(c.titlebar !== false){
52968                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52969                 this.collapseBtn.on("click", this.collapse, this);
52970                 this.collapseBtn.enableDisplayMode();
52971
52972                 if(c.showPin === true || this.showPin){
52973                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52974                     this.stickBtn.enableDisplayMode();
52975                     this.stickBtn.on("click", this.expand, this);
52976                     this.stickBtn.hide();
52977                 }
52978             }
52979             /** This region's collapsed element
52980             * @type Roo.Element */
52981             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52982                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52983             ]}, true);
52984             if(c.floatable !== false){
52985                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52986                this.collapsedEl.on("click", this.collapseClick, this);
52987             }
52988
52989             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52990                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52991                    id: "message", unselectable: "on", style:{"float":"left"}});
52992                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52993              }
52994             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52995             this.expandBtn.on("click", this.expand, this);
52996         }
52997         if(this.collapseBtn){
52998             this.collapseBtn.setVisible(c.collapsible == true);
52999         }
53000         this.cmargins = c.cmargins || this.cmargins ||
53001                          (this.position == "west" || this.position == "east" ?
53002                              {top: 0, left: 2, right:2, bottom: 0} :
53003                              {top: 2, left: 0, right:0, bottom: 2});
53004         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53005         this.bottomTabs = c.tabPosition != "top";
53006         this.autoScroll = c.autoScroll || false;
53007         if(this.autoScroll){
53008             this.bodyEl.setStyle("overflow", "auto");
53009         }else{
53010             this.bodyEl.setStyle("overflow", "hidden");
53011         }
53012         //if(c.titlebar !== false){
53013             if((!c.titlebar && !c.title) || c.titlebar === false){
53014                 this.titleEl.hide();
53015             }else{
53016                 this.titleEl.show();
53017                 if(c.title){
53018                     this.titleTextEl.innerHTML = c.title;
53019                 }
53020             }
53021         //}
53022         this.duration = c.duration || .30;
53023         this.slideDuration = c.slideDuration || .45;
53024         this.config = c;
53025         if(c.collapsed){
53026             this.collapse(true);
53027         }
53028         if(c.hidden){
53029             this.hide();
53030         }
53031     },
53032     /**
53033      * Returns true if this region is currently visible.
53034      * @return {Boolean}
53035      */
53036     isVisible : function(){
53037         return this.visible;
53038     },
53039
53040     /**
53041      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53042      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53043      */
53044     setCollapsedTitle : function(title){
53045         title = title || "&#160;";
53046         if(this.collapsedTitleTextEl){
53047             this.collapsedTitleTextEl.innerHTML = title;
53048         }
53049     },
53050
53051     getBox : function(){
53052         var b;
53053         if(!this.collapsed){
53054             b = this.el.getBox(false, true);
53055         }else{
53056             b = this.collapsedEl.getBox(false, true);
53057         }
53058         return b;
53059     },
53060
53061     getMargins : function(){
53062         return this.collapsed ? this.cmargins : this.margins;
53063     },
53064
53065     highlight : function(){
53066         this.el.addClass("x-layout-panel-dragover");
53067     },
53068
53069     unhighlight : function(){
53070         this.el.removeClass("x-layout-panel-dragover");
53071     },
53072
53073     updateBox : function(box){
53074         this.box = box;
53075         if(!this.collapsed){
53076             this.el.dom.style.left = box.x + "px";
53077             this.el.dom.style.top = box.y + "px";
53078             this.updateBody(box.width, box.height);
53079         }else{
53080             this.collapsedEl.dom.style.left = box.x + "px";
53081             this.collapsedEl.dom.style.top = box.y + "px";
53082             this.collapsedEl.setSize(box.width, box.height);
53083         }
53084         if(this.tabs){
53085             this.tabs.autoSizeTabs();
53086         }
53087     },
53088
53089     updateBody : function(w, h){
53090         if(w !== null){
53091             this.el.setWidth(w);
53092             w -= this.el.getBorderWidth("rl");
53093             if(this.config.adjustments){
53094                 w += this.config.adjustments[0];
53095             }
53096         }
53097         if(h !== null){
53098             this.el.setHeight(h);
53099             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53100             h -= this.el.getBorderWidth("tb");
53101             if(this.config.adjustments){
53102                 h += this.config.adjustments[1];
53103             }
53104             this.bodyEl.setHeight(h);
53105             if(this.tabs){
53106                 h = this.tabs.syncHeight(h);
53107             }
53108         }
53109         if(this.panelSize){
53110             w = w !== null ? w : this.panelSize.width;
53111             h = h !== null ? h : this.panelSize.height;
53112         }
53113         if(this.activePanel){
53114             var el = this.activePanel.getEl();
53115             w = w !== null ? w : el.getWidth();
53116             h = h !== null ? h : el.getHeight();
53117             this.panelSize = {width: w, height: h};
53118             this.activePanel.setSize(w, h);
53119         }
53120         if(Roo.isIE && this.tabs){
53121             this.tabs.el.repaint();
53122         }
53123     },
53124
53125     /**
53126      * Returns the container element for this region.
53127      * @return {Roo.Element}
53128      */
53129     getEl : function(){
53130         return this.el;
53131     },
53132
53133     /**
53134      * Hides this region.
53135      */
53136     hide : function(){
53137         if(!this.collapsed){
53138             this.el.dom.style.left = "-2000px";
53139             this.el.hide();
53140         }else{
53141             this.collapsedEl.dom.style.left = "-2000px";
53142             this.collapsedEl.hide();
53143         }
53144         this.visible = false;
53145         this.fireEvent("visibilitychange", this, false);
53146     },
53147
53148     /**
53149      * Shows this region if it was previously hidden.
53150      */
53151     show : function(){
53152         if(!this.collapsed){
53153             this.el.show();
53154         }else{
53155             this.collapsedEl.show();
53156         }
53157         this.visible = true;
53158         this.fireEvent("visibilitychange", this, true);
53159     },
53160
53161     closeClicked : function(){
53162         if(this.activePanel){
53163             this.remove(this.activePanel);
53164         }
53165     },
53166
53167     collapseClick : function(e){
53168         if(this.isSlid){
53169            e.stopPropagation();
53170            this.slideIn();
53171         }else{
53172            e.stopPropagation();
53173            this.slideOut();
53174         }
53175     },
53176
53177     /**
53178      * Collapses this region.
53179      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53180      */
53181     collapse : function(skipAnim, skipCheck){
53182         if(this.collapsed) {
53183             return;
53184         }
53185         
53186         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53187             
53188             this.collapsed = true;
53189             if(this.split){
53190                 this.split.el.hide();
53191             }
53192             if(this.config.animate && skipAnim !== true){
53193                 this.fireEvent("invalidated", this);
53194                 this.animateCollapse();
53195             }else{
53196                 this.el.setLocation(-20000,-20000);
53197                 this.el.hide();
53198                 this.collapsedEl.show();
53199                 this.fireEvent("collapsed", this);
53200                 this.fireEvent("invalidated", this);
53201             }
53202         }
53203         
53204     },
53205
53206     animateCollapse : function(){
53207         // overridden
53208     },
53209
53210     /**
53211      * Expands this region if it was previously collapsed.
53212      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53213      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53214      */
53215     expand : function(e, skipAnim){
53216         if(e) {
53217             e.stopPropagation();
53218         }
53219         if(!this.collapsed || this.el.hasActiveFx()) {
53220             return;
53221         }
53222         if(this.isSlid){
53223             this.afterSlideIn();
53224             skipAnim = true;
53225         }
53226         this.collapsed = false;
53227         if(this.config.animate && skipAnim !== true){
53228             this.animateExpand();
53229         }else{
53230             this.el.show();
53231             if(this.split){
53232                 this.split.el.show();
53233             }
53234             this.collapsedEl.setLocation(-2000,-2000);
53235             this.collapsedEl.hide();
53236             this.fireEvent("invalidated", this);
53237             this.fireEvent("expanded", this);
53238         }
53239     },
53240
53241     animateExpand : function(){
53242         // overridden
53243     },
53244
53245     initTabs : function()
53246     {
53247         this.bodyEl.setStyle("overflow", "hidden");
53248         var ts = new Roo.TabPanel(
53249                 this.bodyEl.dom,
53250                 {
53251                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53252                     disableTooltips: this.config.disableTabTips,
53253                     toolbar : this.config.toolbar
53254                 }
53255         );
53256         if(this.config.hideTabs){
53257             ts.stripWrap.setDisplayed(false);
53258         }
53259         this.tabs = ts;
53260         ts.resizeTabs = this.config.resizeTabs === true;
53261         ts.minTabWidth = this.config.minTabWidth || 40;
53262         ts.maxTabWidth = this.config.maxTabWidth || 250;
53263         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53264         ts.monitorResize = false;
53265         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53266         ts.bodyEl.addClass('x-layout-tabs-body');
53267         this.panels.each(this.initPanelAsTab, this);
53268     },
53269
53270     initPanelAsTab : function(panel){
53271         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53272                     this.config.closeOnTab && panel.isClosable());
53273         if(panel.tabTip !== undefined){
53274             ti.setTooltip(panel.tabTip);
53275         }
53276         ti.on("activate", function(){
53277               this.setActivePanel(panel);
53278         }, this);
53279         if(this.config.closeOnTab){
53280             ti.on("beforeclose", function(t, e){
53281                 e.cancel = true;
53282                 this.remove(panel);
53283             }, this);
53284         }
53285         return ti;
53286     },
53287
53288     updatePanelTitle : function(panel, title){
53289         if(this.activePanel == panel){
53290             this.updateTitle(title);
53291         }
53292         if(this.tabs){
53293             var ti = this.tabs.getTab(panel.getEl().id);
53294             ti.setText(title);
53295             if(panel.tabTip !== undefined){
53296                 ti.setTooltip(panel.tabTip);
53297             }
53298         }
53299     },
53300
53301     updateTitle : function(title){
53302         if(this.titleTextEl && !this.config.title){
53303             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53304         }
53305     },
53306
53307     setActivePanel : function(panel){
53308         panel = this.getPanel(panel);
53309         if(this.activePanel && this.activePanel != panel){
53310             this.activePanel.setActiveState(false);
53311         }
53312         this.activePanel = panel;
53313         panel.setActiveState(true);
53314         if(this.panelSize){
53315             panel.setSize(this.panelSize.width, this.panelSize.height);
53316         }
53317         if(this.closeBtn){
53318             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53319         }
53320         this.updateTitle(panel.getTitle());
53321         if(this.tabs){
53322             this.fireEvent("invalidated", this);
53323         }
53324         this.fireEvent("panelactivated", this, panel);
53325     },
53326
53327     /**
53328      * Shows the specified panel.
53329      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53330      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53331      */
53332     showPanel : function(panel)
53333     {
53334         panel = this.getPanel(panel);
53335         if(panel){
53336             if(this.tabs){
53337                 var tab = this.tabs.getTab(panel.getEl().id);
53338                 if(tab.isHidden()){
53339                     this.tabs.unhideTab(tab.id);
53340                 }
53341                 tab.activate();
53342             }else{
53343                 this.setActivePanel(panel);
53344             }
53345         }
53346         return panel;
53347     },
53348
53349     /**
53350      * Get the active panel for this region.
53351      * @return {Roo.ContentPanel} The active panel or null
53352      */
53353     getActivePanel : function(){
53354         return this.activePanel;
53355     },
53356
53357     validateVisibility : function(){
53358         if(this.panels.getCount() < 1){
53359             this.updateTitle("&#160;");
53360             this.closeBtn.hide();
53361             this.hide();
53362         }else{
53363             if(!this.isVisible()){
53364                 this.show();
53365             }
53366         }
53367     },
53368
53369     /**
53370      * Adds the passed ContentPanel(s) to this region.
53371      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53372      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53373      */
53374     add : function(panel){
53375         if(arguments.length > 1){
53376             for(var i = 0, len = arguments.length; i < len; i++) {
53377                 this.add(arguments[i]);
53378             }
53379             return null;
53380         }
53381         if(this.hasPanel(panel)){
53382             this.showPanel(panel);
53383             return panel;
53384         }
53385         panel.setRegion(this);
53386         this.panels.add(panel);
53387         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53388             this.bodyEl.dom.appendChild(panel.getEl().dom);
53389             if(panel.background !== true){
53390                 this.setActivePanel(panel);
53391             }
53392             this.fireEvent("paneladded", this, panel);
53393             return panel;
53394         }
53395         if(!this.tabs){
53396             this.initTabs();
53397         }else{
53398             this.initPanelAsTab(panel);
53399         }
53400         if(panel.background !== true){
53401             this.tabs.activate(panel.getEl().id);
53402         }
53403         this.fireEvent("paneladded", this, panel);
53404         return panel;
53405     },
53406
53407     /**
53408      * Hides the tab for the specified panel.
53409      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53410      */
53411     hidePanel : function(panel){
53412         if(this.tabs && (panel = this.getPanel(panel))){
53413             this.tabs.hideTab(panel.getEl().id);
53414         }
53415     },
53416
53417     /**
53418      * Unhides the tab for a previously hidden panel.
53419      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53420      */
53421     unhidePanel : function(panel){
53422         if(this.tabs && (panel = this.getPanel(panel))){
53423             this.tabs.unhideTab(panel.getEl().id);
53424         }
53425     },
53426
53427     clearPanels : function(){
53428         while(this.panels.getCount() > 0){
53429              this.remove(this.panels.first());
53430         }
53431     },
53432
53433     /**
53434      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53435      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53436      * @param {Boolean} preservePanel Overrides the config preservePanel option
53437      * @return {Roo.ContentPanel} The panel that was removed
53438      */
53439     remove : function(panel, preservePanel){
53440         panel = this.getPanel(panel);
53441         if(!panel){
53442             return null;
53443         }
53444         var e = {};
53445         this.fireEvent("beforeremove", this, panel, e);
53446         if(e.cancel === true){
53447             return null;
53448         }
53449         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53450         var panelId = panel.getId();
53451         this.panels.removeKey(panelId);
53452         if(preservePanel){
53453             document.body.appendChild(panel.getEl().dom);
53454         }
53455         if(this.tabs){
53456             this.tabs.removeTab(panel.getEl().id);
53457         }else if (!preservePanel){
53458             this.bodyEl.dom.removeChild(panel.getEl().dom);
53459         }
53460         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53461             var p = this.panels.first();
53462             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53463             tempEl.appendChild(p.getEl().dom);
53464             this.bodyEl.update("");
53465             this.bodyEl.dom.appendChild(p.getEl().dom);
53466             tempEl = null;
53467             this.updateTitle(p.getTitle());
53468             this.tabs = null;
53469             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53470             this.setActivePanel(p);
53471         }
53472         panel.setRegion(null);
53473         if(this.activePanel == panel){
53474             this.activePanel = null;
53475         }
53476         if(this.config.autoDestroy !== false && preservePanel !== true){
53477             try{panel.destroy();}catch(e){}
53478         }
53479         this.fireEvent("panelremoved", this, panel);
53480         return panel;
53481     },
53482
53483     /**
53484      * Returns the TabPanel component used by this region
53485      * @return {Roo.TabPanel}
53486      */
53487     getTabs : function(){
53488         return this.tabs;
53489     },
53490
53491     createTool : function(parentEl, className){
53492         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53493             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53494         btn.addClassOnOver("x-layout-tools-button-over");
53495         return btn;
53496     }
53497 });/*
53498  * Based on:
53499  * Ext JS Library 1.1.1
53500  * Copyright(c) 2006-2007, Ext JS, LLC.
53501  *
53502  * Originally Released Under LGPL - original licence link has changed is not relivant.
53503  *
53504  * Fork - LGPL
53505  * <script type="text/javascript">
53506  */
53507  
53508
53509
53510 /**
53511  * @class Roo.SplitLayoutRegion
53512  * @extends Roo.LayoutRegion
53513  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53514  */
53515 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53516     this.cursor = cursor;
53517     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53518 };
53519
53520 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53521     splitTip : "Drag to resize.",
53522     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53523     useSplitTips : false,
53524
53525     applyConfig : function(config){
53526         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53527         if(config.split){
53528             if(!this.split){
53529                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53530                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53531                 /** The SplitBar for this region 
53532                 * @type Roo.SplitBar */
53533                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53534                 this.split.on("moved", this.onSplitMove, this);
53535                 this.split.useShim = config.useShim === true;
53536                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53537                 if(this.useSplitTips){
53538                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53539                 }
53540                 if(config.collapsible){
53541                     this.split.el.on("dblclick", this.collapse,  this);
53542                 }
53543             }
53544             if(typeof config.minSize != "undefined"){
53545                 this.split.minSize = config.minSize;
53546             }
53547             if(typeof config.maxSize != "undefined"){
53548                 this.split.maxSize = config.maxSize;
53549             }
53550             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53551                 this.hideSplitter();
53552             }
53553         }
53554     },
53555
53556     getHMaxSize : function(){
53557          var cmax = this.config.maxSize || 10000;
53558          var center = this.mgr.getRegion("center");
53559          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53560     },
53561
53562     getVMaxSize : function(){
53563          var cmax = this.config.maxSize || 10000;
53564          var center = this.mgr.getRegion("center");
53565          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53566     },
53567
53568     onSplitMove : function(split, newSize){
53569         this.fireEvent("resized", this, newSize);
53570     },
53571     
53572     /** 
53573      * Returns the {@link Roo.SplitBar} for this region.
53574      * @return {Roo.SplitBar}
53575      */
53576     getSplitBar : function(){
53577         return this.split;
53578     },
53579     
53580     hide : function(){
53581         this.hideSplitter();
53582         Roo.SplitLayoutRegion.superclass.hide.call(this);
53583     },
53584
53585     hideSplitter : function(){
53586         if(this.split){
53587             this.split.el.setLocation(-2000,-2000);
53588             this.split.el.hide();
53589         }
53590     },
53591
53592     show : function(){
53593         if(this.split){
53594             this.split.el.show();
53595         }
53596         Roo.SplitLayoutRegion.superclass.show.call(this);
53597     },
53598     
53599     beforeSlide: function(){
53600         if(Roo.isGecko){// firefox overflow auto bug workaround
53601             this.bodyEl.clip();
53602             if(this.tabs) {
53603                 this.tabs.bodyEl.clip();
53604             }
53605             if(this.activePanel){
53606                 this.activePanel.getEl().clip();
53607                 
53608                 if(this.activePanel.beforeSlide){
53609                     this.activePanel.beforeSlide();
53610                 }
53611             }
53612         }
53613     },
53614     
53615     afterSlide : function(){
53616         if(Roo.isGecko){// firefox overflow auto bug workaround
53617             this.bodyEl.unclip();
53618             if(this.tabs) {
53619                 this.tabs.bodyEl.unclip();
53620             }
53621             if(this.activePanel){
53622                 this.activePanel.getEl().unclip();
53623                 if(this.activePanel.afterSlide){
53624                     this.activePanel.afterSlide();
53625                 }
53626             }
53627         }
53628     },
53629
53630     initAutoHide : function(){
53631         if(this.autoHide !== false){
53632             if(!this.autoHideHd){
53633                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53634                 this.autoHideHd = {
53635                     "mouseout": function(e){
53636                         if(!e.within(this.el, true)){
53637                             st.delay(500);
53638                         }
53639                     },
53640                     "mouseover" : function(e){
53641                         st.cancel();
53642                     },
53643                     scope : this
53644                 };
53645             }
53646             this.el.on(this.autoHideHd);
53647         }
53648     },
53649
53650     clearAutoHide : function(){
53651         if(this.autoHide !== false){
53652             this.el.un("mouseout", this.autoHideHd.mouseout);
53653             this.el.un("mouseover", this.autoHideHd.mouseover);
53654         }
53655     },
53656
53657     clearMonitor : function(){
53658         Roo.get(document).un("click", this.slideInIf, this);
53659     },
53660
53661     // these names are backwards but not changed for compat
53662     slideOut : function(){
53663         if(this.isSlid || this.el.hasActiveFx()){
53664             return;
53665         }
53666         this.isSlid = true;
53667         if(this.collapseBtn){
53668             this.collapseBtn.hide();
53669         }
53670         this.closeBtnState = this.closeBtn.getStyle('display');
53671         this.closeBtn.hide();
53672         if(this.stickBtn){
53673             this.stickBtn.show();
53674         }
53675         this.el.show();
53676         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53677         this.beforeSlide();
53678         this.el.setStyle("z-index", 10001);
53679         this.el.slideIn(this.getSlideAnchor(), {
53680             callback: function(){
53681                 this.afterSlide();
53682                 this.initAutoHide();
53683                 Roo.get(document).on("click", this.slideInIf, this);
53684                 this.fireEvent("slideshow", this);
53685             },
53686             scope: this,
53687             block: true
53688         });
53689     },
53690
53691     afterSlideIn : function(){
53692         this.clearAutoHide();
53693         this.isSlid = false;
53694         this.clearMonitor();
53695         this.el.setStyle("z-index", "");
53696         if(this.collapseBtn){
53697             this.collapseBtn.show();
53698         }
53699         this.closeBtn.setStyle('display', this.closeBtnState);
53700         if(this.stickBtn){
53701             this.stickBtn.hide();
53702         }
53703         this.fireEvent("slidehide", this);
53704     },
53705
53706     slideIn : function(cb){
53707         if(!this.isSlid || this.el.hasActiveFx()){
53708             Roo.callback(cb);
53709             return;
53710         }
53711         this.isSlid = false;
53712         this.beforeSlide();
53713         this.el.slideOut(this.getSlideAnchor(), {
53714             callback: function(){
53715                 this.el.setLeftTop(-10000, -10000);
53716                 this.afterSlide();
53717                 this.afterSlideIn();
53718                 Roo.callback(cb);
53719             },
53720             scope: this,
53721             block: true
53722         });
53723     },
53724     
53725     slideInIf : function(e){
53726         if(!e.within(this.el)){
53727             this.slideIn();
53728         }
53729     },
53730
53731     animateCollapse : function(){
53732         this.beforeSlide();
53733         this.el.setStyle("z-index", 20000);
53734         var anchor = this.getSlideAnchor();
53735         this.el.slideOut(anchor, {
53736             callback : function(){
53737                 this.el.setStyle("z-index", "");
53738                 this.collapsedEl.slideIn(anchor, {duration:.3});
53739                 this.afterSlide();
53740                 this.el.setLocation(-10000,-10000);
53741                 this.el.hide();
53742                 this.fireEvent("collapsed", this);
53743             },
53744             scope: this,
53745             block: true
53746         });
53747     },
53748
53749     animateExpand : function(){
53750         this.beforeSlide();
53751         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53752         this.el.setStyle("z-index", 20000);
53753         this.collapsedEl.hide({
53754             duration:.1
53755         });
53756         this.el.slideIn(this.getSlideAnchor(), {
53757             callback : function(){
53758                 this.el.setStyle("z-index", "");
53759                 this.afterSlide();
53760                 if(this.split){
53761                     this.split.el.show();
53762                 }
53763                 this.fireEvent("invalidated", this);
53764                 this.fireEvent("expanded", this);
53765             },
53766             scope: this,
53767             block: true
53768         });
53769     },
53770
53771     anchors : {
53772         "west" : "left",
53773         "east" : "right",
53774         "north" : "top",
53775         "south" : "bottom"
53776     },
53777
53778     sanchors : {
53779         "west" : "l",
53780         "east" : "r",
53781         "north" : "t",
53782         "south" : "b"
53783     },
53784
53785     canchors : {
53786         "west" : "tl-tr",
53787         "east" : "tr-tl",
53788         "north" : "tl-bl",
53789         "south" : "bl-tl"
53790     },
53791
53792     getAnchor : function(){
53793         return this.anchors[this.position];
53794     },
53795
53796     getCollapseAnchor : function(){
53797         return this.canchors[this.position];
53798     },
53799
53800     getSlideAnchor : function(){
53801         return this.sanchors[this.position];
53802     },
53803
53804     getAlignAdj : function(){
53805         var cm = this.cmargins;
53806         switch(this.position){
53807             case "west":
53808                 return [0, 0];
53809             break;
53810             case "east":
53811                 return [0, 0];
53812             break;
53813             case "north":
53814                 return [0, 0];
53815             break;
53816             case "south":
53817                 return [0, 0];
53818             break;
53819         }
53820     },
53821
53822     getExpandAdj : function(){
53823         var c = this.collapsedEl, cm = this.cmargins;
53824         switch(this.position){
53825             case "west":
53826                 return [-(cm.right+c.getWidth()+cm.left), 0];
53827             break;
53828             case "east":
53829                 return [cm.right+c.getWidth()+cm.left, 0];
53830             break;
53831             case "north":
53832                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53833             break;
53834             case "south":
53835                 return [0, cm.top+cm.bottom+c.getHeight()];
53836             break;
53837         }
53838     }
53839 });/*
53840  * Based on:
53841  * Ext JS Library 1.1.1
53842  * Copyright(c) 2006-2007, Ext JS, LLC.
53843  *
53844  * Originally Released Under LGPL - original licence link has changed is not relivant.
53845  *
53846  * Fork - LGPL
53847  * <script type="text/javascript">
53848  */
53849 /*
53850  * These classes are private internal classes
53851  */
53852 Roo.CenterLayoutRegion = function(mgr, config){
53853     Roo.LayoutRegion.call(this, mgr, config, "center");
53854     this.visible = true;
53855     this.minWidth = config.minWidth || 20;
53856     this.minHeight = config.minHeight || 20;
53857 };
53858
53859 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53860     hide : function(){
53861         // center panel can't be hidden
53862     },
53863     
53864     show : function(){
53865         // center panel can't be hidden
53866     },
53867     
53868     getMinWidth: function(){
53869         return this.minWidth;
53870     },
53871     
53872     getMinHeight: function(){
53873         return this.minHeight;
53874     }
53875 });
53876
53877
53878 Roo.NorthLayoutRegion = function(mgr, config){
53879     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53880     if(this.split){
53881         this.split.placement = Roo.SplitBar.TOP;
53882         this.split.orientation = Roo.SplitBar.VERTICAL;
53883         this.split.el.addClass("x-layout-split-v");
53884     }
53885     var size = config.initialSize || config.height;
53886     if(typeof size != "undefined"){
53887         this.el.setHeight(size);
53888     }
53889 };
53890 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53891     orientation: Roo.SplitBar.VERTICAL,
53892     getBox : function(){
53893         if(this.collapsed){
53894             return this.collapsedEl.getBox();
53895         }
53896         var box = this.el.getBox();
53897         if(this.split){
53898             box.height += this.split.el.getHeight();
53899         }
53900         return box;
53901     },
53902     
53903     updateBox : function(box){
53904         if(this.split && !this.collapsed){
53905             box.height -= this.split.el.getHeight();
53906             this.split.el.setLeft(box.x);
53907             this.split.el.setTop(box.y+box.height);
53908             this.split.el.setWidth(box.width);
53909         }
53910         if(this.collapsed){
53911             this.updateBody(box.width, null);
53912         }
53913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53914     }
53915 });
53916
53917 Roo.SouthLayoutRegion = function(mgr, config){
53918     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53919     if(this.split){
53920         this.split.placement = Roo.SplitBar.BOTTOM;
53921         this.split.orientation = Roo.SplitBar.VERTICAL;
53922         this.split.el.addClass("x-layout-split-v");
53923     }
53924     var size = config.initialSize || config.height;
53925     if(typeof size != "undefined"){
53926         this.el.setHeight(size);
53927     }
53928 };
53929 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53930     orientation: Roo.SplitBar.VERTICAL,
53931     getBox : function(){
53932         if(this.collapsed){
53933             return this.collapsedEl.getBox();
53934         }
53935         var box = this.el.getBox();
53936         if(this.split){
53937             var sh = this.split.el.getHeight();
53938             box.height += sh;
53939             box.y -= sh;
53940         }
53941         return box;
53942     },
53943     
53944     updateBox : function(box){
53945         if(this.split && !this.collapsed){
53946             var sh = this.split.el.getHeight();
53947             box.height -= sh;
53948             box.y += sh;
53949             this.split.el.setLeft(box.x);
53950             this.split.el.setTop(box.y-sh);
53951             this.split.el.setWidth(box.width);
53952         }
53953         if(this.collapsed){
53954             this.updateBody(box.width, null);
53955         }
53956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53957     }
53958 });
53959
53960 Roo.EastLayoutRegion = function(mgr, config){
53961     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53962     if(this.split){
53963         this.split.placement = Roo.SplitBar.RIGHT;
53964         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53965         this.split.el.addClass("x-layout-split-h");
53966     }
53967     var size = config.initialSize || config.width;
53968     if(typeof size != "undefined"){
53969         this.el.setWidth(size);
53970     }
53971 };
53972 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53973     orientation: Roo.SplitBar.HORIZONTAL,
53974     getBox : function(){
53975         if(this.collapsed){
53976             return this.collapsedEl.getBox();
53977         }
53978         var box = this.el.getBox();
53979         if(this.split){
53980             var sw = this.split.el.getWidth();
53981             box.width += sw;
53982             box.x -= sw;
53983         }
53984         return box;
53985     },
53986
53987     updateBox : function(box){
53988         if(this.split && !this.collapsed){
53989             var sw = this.split.el.getWidth();
53990             box.width -= sw;
53991             this.split.el.setLeft(box.x);
53992             this.split.el.setTop(box.y);
53993             this.split.el.setHeight(box.height);
53994             box.x += sw;
53995         }
53996         if(this.collapsed){
53997             this.updateBody(null, box.height);
53998         }
53999         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54000     }
54001 });
54002
54003 Roo.WestLayoutRegion = function(mgr, config){
54004     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54005     if(this.split){
54006         this.split.placement = Roo.SplitBar.LEFT;
54007         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54008         this.split.el.addClass("x-layout-split-h");
54009     }
54010     var size = config.initialSize || config.width;
54011     if(typeof size != "undefined"){
54012         this.el.setWidth(size);
54013     }
54014 };
54015 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54016     orientation: Roo.SplitBar.HORIZONTAL,
54017     getBox : function(){
54018         if(this.collapsed){
54019             return this.collapsedEl.getBox();
54020         }
54021         var box = this.el.getBox();
54022         if(this.split){
54023             box.width += this.split.el.getWidth();
54024         }
54025         return box;
54026     },
54027     
54028     updateBox : function(box){
54029         if(this.split && !this.collapsed){
54030             var sw = this.split.el.getWidth();
54031             box.width -= sw;
54032             this.split.el.setLeft(box.x+box.width);
54033             this.split.el.setTop(box.y);
54034             this.split.el.setHeight(box.height);
54035         }
54036         if(this.collapsed){
54037             this.updateBody(null, box.height);
54038         }
54039         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54040     }
54041 });
54042 /*
54043  * Based on:
54044  * Ext JS Library 1.1.1
54045  * Copyright(c) 2006-2007, Ext JS, LLC.
54046  *
54047  * Originally Released Under LGPL - original licence link has changed is not relivant.
54048  *
54049  * Fork - LGPL
54050  * <script type="text/javascript">
54051  */
54052  
54053  
54054 /*
54055  * Private internal class for reading and applying state
54056  */
54057 Roo.LayoutStateManager = function(layout){
54058      // default empty state
54059      this.state = {
54060         north: {},
54061         south: {},
54062         east: {},
54063         west: {}       
54064     };
54065 };
54066
54067 Roo.LayoutStateManager.prototype = {
54068     init : function(layout, provider){
54069         this.provider = provider;
54070         var state = provider.get(layout.id+"-layout-state");
54071         if(state){
54072             var wasUpdating = layout.isUpdating();
54073             if(!wasUpdating){
54074                 layout.beginUpdate();
54075             }
54076             for(var key in state){
54077                 if(typeof state[key] != "function"){
54078                     var rstate = state[key];
54079                     var r = layout.getRegion(key);
54080                     if(r && rstate){
54081                         if(rstate.size){
54082                             r.resizeTo(rstate.size);
54083                         }
54084                         if(rstate.collapsed == true){
54085                             r.collapse(true);
54086                         }else{
54087                             r.expand(null, true);
54088                         }
54089                     }
54090                 }
54091             }
54092             if(!wasUpdating){
54093                 layout.endUpdate();
54094             }
54095             this.state = state; 
54096         }
54097         this.layout = layout;
54098         layout.on("regionresized", this.onRegionResized, this);
54099         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54100         layout.on("regionexpanded", this.onRegionExpanded, this);
54101     },
54102     
54103     storeState : function(){
54104         this.provider.set(this.layout.id+"-layout-state", this.state);
54105     },
54106     
54107     onRegionResized : function(region, newSize){
54108         this.state[region.getPosition()].size = newSize;
54109         this.storeState();
54110     },
54111     
54112     onRegionCollapsed : function(region){
54113         this.state[region.getPosition()].collapsed = true;
54114         this.storeState();
54115     },
54116     
54117     onRegionExpanded : function(region){
54118         this.state[region.getPosition()].collapsed = false;
54119         this.storeState();
54120     }
54121 };/*
54122  * Based on:
54123  * Ext JS Library 1.1.1
54124  * Copyright(c) 2006-2007, Ext JS, LLC.
54125  *
54126  * Originally Released Under LGPL - original licence link has changed is not relivant.
54127  *
54128  * Fork - LGPL
54129  * <script type="text/javascript">
54130  */
54131 /**
54132  * @class Roo.ContentPanel
54133  * @extends Roo.util.Observable
54134  * A basic ContentPanel element.
54135  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54136  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54137  * @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
54138  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54139  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54140  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54141  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54142  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54143  * @cfg {String} title          The title for this panel
54144  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54145  * @cfg {String} url            Calls {@link #setUrl} with this value
54146  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54147  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54148  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54149  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54150  * @cfg {String}    style  Extra style to add to the content panel 
54151
54152  * @constructor
54153  * Create a new ContentPanel.
54154  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54155  * @param {String/Object} config A string to set only the title or a config object
54156  * @param {String} content (optional) Set the HTML content for this panel
54157  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54158  */
54159 Roo.ContentPanel = function(el, config, content){
54160     
54161      
54162     /*
54163     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54164         config = el;
54165         el = Roo.id();
54166     }
54167     if (config && config.parentLayout) { 
54168         el = config.parentLayout.el.createChild(); 
54169     }
54170     */
54171     if(el.autoCreate){ // xtype is available if this is called from factory
54172         config = el;
54173         el = Roo.id();
54174     }
54175     this.el = Roo.get(el);
54176     if(!this.el && config && config.autoCreate){
54177         if(typeof config.autoCreate == "object"){
54178             if(!config.autoCreate.id){
54179                 config.autoCreate.id = config.id||el;
54180             }
54181             this.el = Roo.DomHelper.append(document.body,
54182                         config.autoCreate, true);
54183         }else{
54184             this.el = Roo.DomHelper.append(document.body,
54185                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54186         }
54187     }
54188     
54189     
54190     this.closable = false;
54191     this.loaded = false;
54192     this.active = false;
54193     if(typeof config == "string"){
54194         this.title = config;
54195     }else{
54196         Roo.apply(this, config);
54197     }
54198     
54199     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54200         this.wrapEl = this.el.wrap();
54201         this.toolbar.container = this.el.insertSibling(false, 'before');
54202         this.toolbar = new Roo.Toolbar(this.toolbar);
54203     }
54204     
54205     // xtype created footer. - not sure if will work as we normally have to render first..
54206     if (this.footer && !this.footer.el && this.footer.xtype) {
54207         if (!this.wrapEl) {
54208             this.wrapEl = this.el.wrap();
54209         }
54210     
54211         this.footer.container = this.wrapEl.createChild();
54212          
54213         this.footer = Roo.factory(this.footer, Roo);
54214         
54215     }
54216     
54217     if(this.resizeEl){
54218         this.resizeEl = Roo.get(this.resizeEl, true);
54219     }else{
54220         this.resizeEl = this.el;
54221     }
54222     // handle view.xtype
54223     
54224  
54225     
54226     
54227     this.addEvents({
54228         /**
54229          * @event activate
54230          * Fires when this panel is activated. 
54231          * @param {Roo.ContentPanel} this
54232          */
54233         "activate" : true,
54234         /**
54235          * @event deactivate
54236          * Fires when this panel is activated. 
54237          * @param {Roo.ContentPanel} this
54238          */
54239         "deactivate" : true,
54240
54241         /**
54242          * @event resize
54243          * Fires when this panel is resized if fitToFrame is true.
54244          * @param {Roo.ContentPanel} this
54245          * @param {Number} width The width after any component adjustments
54246          * @param {Number} height The height after any component adjustments
54247          */
54248         "resize" : true,
54249         
54250          /**
54251          * @event render
54252          * Fires when this tab is created
54253          * @param {Roo.ContentPanel} this
54254          */
54255         "render" : true
54256          
54257         
54258     });
54259     
54260
54261     
54262     
54263     if(this.autoScroll){
54264         this.resizeEl.setStyle("overflow", "auto");
54265     } else {
54266         // fix randome scrolling
54267         this.el.on('scroll', function() {
54268             Roo.log('fix random scolling');
54269             this.scrollTo('top',0); 
54270         });
54271     }
54272     content = content || this.content;
54273     if(content){
54274         this.setContent(content);
54275     }
54276     if(config && config.url){
54277         this.setUrl(this.url, this.params, this.loadOnce);
54278     }
54279     
54280     
54281     
54282     Roo.ContentPanel.superclass.constructor.call(this);
54283     
54284     if (this.view && typeof(this.view.xtype) != 'undefined') {
54285         this.view.el = this.el.appendChild(document.createElement("div"));
54286         this.view = Roo.factory(this.view); 
54287         this.view.render  &&  this.view.render(false, '');  
54288     }
54289     
54290     
54291     this.fireEvent('render', this);
54292 };
54293
54294 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54295     tabTip:'',
54296     setRegion : function(region){
54297         this.region = region;
54298         if(region){
54299            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54300         }else{
54301            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54302         } 
54303     },
54304     
54305     /**
54306      * Returns the toolbar for this Panel if one was configured. 
54307      * @return {Roo.Toolbar} 
54308      */
54309     getToolbar : function(){
54310         return this.toolbar;
54311     },
54312     
54313     setActiveState : function(active){
54314         this.active = active;
54315         if(!active){
54316             this.fireEvent("deactivate", this);
54317         }else{
54318             this.fireEvent("activate", this);
54319         }
54320     },
54321     /**
54322      * Updates this panel's element
54323      * @param {String} content The new content
54324      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54325     */
54326     setContent : function(content, loadScripts){
54327         this.el.update(content, loadScripts);
54328     },
54329
54330     ignoreResize : function(w, h){
54331         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54332             return true;
54333         }else{
54334             this.lastSize = {width: w, height: h};
54335             return false;
54336         }
54337     },
54338     /**
54339      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54340      * @return {Roo.UpdateManager} The UpdateManager
54341      */
54342     getUpdateManager : function(){
54343         return this.el.getUpdateManager();
54344     },
54345      /**
54346      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54347      * @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:
54348 <pre><code>
54349 panel.load({
54350     url: "your-url.php",
54351     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54352     callback: yourFunction,
54353     scope: yourObject, //(optional scope)
54354     discardUrl: false,
54355     nocache: false,
54356     text: "Loading...",
54357     timeout: 30,
54358     scripts: false
54359 });
54360 </code></pre>
54361      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54362      * 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.
54363      * @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}
54364      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54365      * @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.
54366      * @return {Roo.ContentPanel} this
54367      */
54368     load : function(){
54369         var um = this.el.getUpdateManager();
54370         um.update.apply(um, arguments);
54371         return this;
54372     },
54373
54374
54375     /**
54376      * 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.
54377      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54378      * @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)
54379      * @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)
54380      * @return {Roo.UpdateManager} The UpdateManager
54381      */
54382     setUrl : function(url, params, loadOnce){
54383         if(this.refreshDelegate){
54384             this.removeListener("activate", this.refreshDelegate);
54385         }
54386         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54387         this.on("activate", this.refreshDelegate);
54388         return this.el.getUpdateManager();
54389     },
54390     
54391     _handleRefresh : function(url, params, loadOnce){
54392         if(!loadOnce || !this.loaded){
54393             var updater = this.el.getUpdateManager();
54394             updater.update(url, params, this._setLoaded.createDelegate(this));
54395         }
54396     },
54397     
54398     _setLoaded : function(){
54399         this.loaded = true;
54400     }, 
54401     
54402     /**
54403      * Returns this panel's id
54404      * @return {String} 
54405      */
54406     getId : function(){
54407         return this.el.id;
54408     },
54409     
54410     /** 
54411      * Returns this panel's element - used by regiosn to add.
54412      * @return {Roo.Element} 
54413      */
54414     getEl : function(){
54415         return this.wrapEl || this.el;
54416     },
54417     
54418     adjustForComponents : function(width, height)
54419     {
54420         //Roo.log('adjustForComponents ');
54421         if(this.resizeEl != this.el){
54422             width -= this.el.getFrameWidth('lr');
54423             height -= this.el.getFrameWidth('tb');
54424         }
54425         if(this.toolbar){
54426             var te = this.toolbar.getEl();
54427             height -= te.getHeight();
54428             te.setWidth(width);
54429         }
54430         if(this.footer){
54431             var te = this.footer.getEl();
54432             //Roo.log("footer:" + te.getHeight());
54433             
54434             height -= te.getHeight();
54435             te.setWidth(width);
54436         }
54437         
54438         
54439         if(this.adjustments){
54440             width += this.adjustments[0];
54441             height += this.adjustments[1];
54442         }
54443         return {"width": width, "height": height};
54444     },
54445     
54446     setSize : function(width, height){
54447         if(this.fitToFrame && !this.ignoreResize(width, height)){
54448             if(this.fitContainer && this.resizeEl != this.el){
54449                 this.el.setSize(width, height);
54450             }
54451             var size = this.adjustForComponents(width, height);
54452             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54453             this.fireEvent('resize', this, size.width, size.height);
54454         }
54455     },
54456     
54457     /**
54458      * Returns this panel's title
54459      * @return {String} 
54460      */
54461     getTitle : function(){
54462         return this.title;
54463     },
54464     
54465     /**
54466      * Set this panel's title
54467      * @param {String} title
54468      */
54469     setTitle : function(title){
54470         this.title = title;
54471         if(this.region){
54472             this.region.updatePanelTitle(this, title);
54473         }
54474     },
54475     
54476     /**
54477      * Returns true is this panel was configured to be closable
54478      * @return {Boolean} 
54479      */
54480     isClosable : function(){
54481         return this.closable;
54482     },
54483     
54484     beforeSlide : function(){
54485         this.el.clip();
54486         this.resizeEl.clip();
54487     },
54488     
54489     afterSlide : function(){
54490         this.el.unclip();
54491         this.resizeEl.unclip();
54492     },
54493     
54494     /**
54495      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54496      *   Will fail silently if the {@link #setUrl} method has not been called.
54497      *   This does not activate the panel, just updates its content.
54498      */
54499     refresh : function(){
54500         if(this.refreshDelegate){
54501            this.loaded = false;
54502            this.refreshDelegate();
54503         }
54504     },
54505     
54506     /**
54507      * Destroys this panel
54508      */
54509     destroy : function(){
54510         this.el.removeAllListeners();
54511         var tempEl = document.createElement("span");
54512         tempEl.appendChild(this.el.dom);
54513         tempEl.innerHTML = "";
54514         this.el.remove();
54515         this.el = null;
54516     },
54517     
54518     /**
54519      * form - if the content panel contains a form - this is a reference to it.
54520      * @type {Roo.form.Form}
54521      */
54522     form : false,
54523     /**
54524      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54525      *    This contains a reference to it.
54526      * @type {Roo.View}
54527      */
54528     view : false,
54529     
54530       /**
54531      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54532      * <pre><code>
54533
54534 layout.addxtype({
54535        xtype : 'Form',
54536        items: [ .... ]
54537    }
54538 );
54539
54540 </code></pre>
54541      * @param {Object} cfg Xtype definition of item to add.
54542      */
54543     
54544     addxtype : function(cfg) {
54545         // add form..
54546         if (cfg.xtype.match(/^Form$/)) {
54547             
54548             var el;
54549             //if (this.footer) {
54550             //    el = this.footer.container.insertSibling(false, 'before');
54551             //} else {
54552                 el = this.el.createChild();
54553             //}
54554
54555             this.form = new  Roo.form.Form(cfg);
54556             
54557             
54558             if ( this.form.allItems.length) {
54559                 this.form.render(el.dom);
54560             }
54561             return this.form;
54562         }
54563         // should only have one of theses..
54564         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54565             // views.. should not be just added - used named prop 'view''
54566             
54567             cfg.el = this.el.appendChild(document.createElement("div"));
54568             // factory?
54569             
54570             var ret = new Roo.factory(cfg);
54571              
54572              ret.render && ret.render(false, ''); // render blank..
54573             this.view = ret;
54574             return ret;
54575         }
54576         return false;
54577     }
54578 });
54579
54580 /**
54581  * @class Roo.GridPanel
54582  * @extends Roo.ContentPanel
54583  * @constructor
54584  * Create a new GridPanel.
54585  * @param {Roo.grid.Grid} grid The grid for this panel
54586  * @param {String/Object} config A string to set only the panel's title, or a config object
54587  */
54588 Roo.GridPanel = function(grid, config){
54589     
54590   
54591     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54592         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54593         
54594     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54595     
54596     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54597     
54598     if(this.toolbar){
54599         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54600     }
54601     // xtype created footer. - not sure if will work as we normally have to render first..
54602     if (this.footer && !this.footer.el && this.footer.xtype) {
54603         
54604         this.footer.container = this.grid.getView().getFooterPanel(true);
54605         this.footer.dataSource = this.grid.dataSource;
54606         this.footer = Roo.factory(this.footer, Roo);
54607         
54608     }
54609     
54610     grid.monitorWindowResize = false; // turn off autosizing
54611     grid.autoHeight = false;
54612     grid.autoWidth = false;
54613     this.grid = grid;
54614     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54615 };
54616
54617 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54618     getId : function(){
54619         return this.grid.id;
54620     },
54621     
54622     /**
54623      * Returns the grid for this panel
54624      * @return {Roo.grid.Grid} 
54625      */
54626     getGrid : function(){
54627         return this.grid;    
54628     },
54629     
54630     setSize : function(width, height){
54631         if(!this.ignoreResize(width, height)){
54632             var grid = this.grid;
54633             var size = this.adjustForComponents(width, height);
54634             grid.getGridEl().setSize(size.width, size.height);
54635             grid.autoSize();
54636         }
54637     },
54638     
54639     beforeSlide : function(){
54640         this.grid.getView().scroller.clip();
54641     },
54642     
54643     afterSlide : function(){
54644         this.grid.getView().scroller.unclip();
54645     },
54646     
54647     destroy : function(){
54648         this.grid.destroy();
54649         delete this.grid;
54650         Roo.GridPanel.superclass.destroy.call(this); 
54651     }
54652 });
54653
54654
54655 /**
54656  * @class Roo.NestedLayoutPanel
54657  * @extends Roo.ContentPanel
54658  * @constructor
54659  * Create a new NestedLayoutPanel.
54660  * 
54661  * 
54662  * @param {Roo.BorderLayout} layout The layout for this panel
54663  * @param {String/Object} config A string to set only the title or a config object
54664  */
54665 Roo.NestedLayoutPanel = function(layout, config)
54666 {
54667     // construct with only one argument..
54668     /* FIXME - implement nicer consturctors
54669     if (layout.layout) {
54670         config = layout;
54671         layout = config.layout;
54672         delete config.layout;
54673     }
54674     if (layout.xtype && !layout.getEl) {
54675         // then layout needs constructing..
54676         layout = Roo.factory(layout, Roo);
54677     }
54678     */
54679     
54680     
54681     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54682     
54683     layout.monitorWindowResize = false; // turn off autosizing
54684     this.layout = layout;
54685     this.layout.getEl().addClass("x-layout-nested-layout");
54686     
54687     
54688     
54689     
54690 };
54691
54692 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54693
54694     setSize : function(width, height){
54695         if(!this.ignoreResize(width, height)){
54696             var size = this.adjustForComponents(width, height);
54697             var el = this.layout.getEl();
54698             el.setSize(size.width, size.height);
54699             var touch = el.dom.offsetWidth;
54700             this.layout.layout();
54701             // ie requires a double layout on the first pass
54702             if(Roo.isIE && !this.initialized){
54703                 this.initialized = true;
54704                 this.layout.layout();
54705             }
54706         }
54707     },
54708     
54709     // activate all subpanels if not currently active..
54710     
54711     setActiveState : function(active){
54712         this.active = active;
54713         if(!active){
54714             this.fireEvent("deactivate", this);
54715             return;
54716         }
54717         
54718         this.fireEvent("activate", this);
54719         // not sure if this should happen before or after..
54720         if (!this.layout) {
54721             return; // should not happen..
54722         }
54723         var reg = false;
54724         for (var r in this.layout.regions) {
54725             reg = this.layout.getRegion(r);
54726             if (reg.getActivePanel()) {
54727                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54728                 reg.setActivePanel(reg.getActivePanel());
54729                 continue;
54730             }
54731             if (!reg.panels.length) {
54732                 continue;
54733             }
54734             reg.showPanel(reg.getPanel(0));
54735         }
54736         
54737         
54738         
54739         
54740     },
54741     
54742     /**
54743      * Returns the nested BorderLayout for this panel
54744      * @return {Roo.BorderLayout} 
54745      */
54746     getLayout : function(){
54747         return this.layout;
54748     },
54749     
54750      /**
54751      * Adds a xtype elements to the layout of the nested panel
54752      * <pre><code>
54753
54754 panel.addxtype({
54755        xtype : 'ContentPanel',
54756        region: 'west',
54757        items: [ .... ]
54758    }
54759 );
54760
54761 panel.addxtype({
54762         xtype : 'NestedLayoutPanel',
54763         region: 'west',
54764         layout: {
54765            center: { },
54766            west: { }   
54767         },
54768         items : [ ... list of content panels or nested layout panels.. ]
54769    }
54770 );
54771 </code></pre>
54772      * @param {Object} cfg Xtype definition of item to add.
54773      */
54774     addxtype : function(cfg) {
54775         return this.layout.addxtype(cfg);
54776     
54777     }
54778 });
54779
54780 Roo.ScrollPanel = function(el, config, content){
54781     config = config || {};
54782     config.fitToFrame = true;
54783     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54784     
54785     this.el.dom.style.overflow = "hidden";
54786     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54787     this.el.removeClass("x-layout-inactive-content");
54788     this.el.on("mousewheel", this.onWheel, this);
54789
54790     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54791     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54792     up.unselectable(); down.unselectable();
54793     up.on("click", this.scrollUp, this);
54794     down.on("click", this.scrollDown, this);
54795     up.addClassOnOver("x-scroller-btn-over");
54796     down.addClassOnOver("x-scroller-btn-over");
54797     up.addClassOnClick("x-scroller-btn-click");
54798     down.addClassOnClick("x-scroller-btn-click");
54799     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54800
54801     this.resizeEl = this.el;
54802     this.el = wrap; this.up = up; this.down = down;
54803 };
54804
54805 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54806     increment : 100,
54807     wheelIncrement : 5,
54808     scrollUp : function(){
54809         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54810     },
54811
54812     scrollDown : function(){
54813         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54814     },
54815
54816     afterScroll : function(){
54817         var el = this.resizeEl;
54818         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54819         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54820         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54821     },
54822
54823     setSize : function(){
54824         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54825         this.afterScroll();
54826     },
54827
54828     onWheel : function(e){
54829         var d = e.getWheelDelta();
54830         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54831         this.afterScroll();
54832         e.stopEvent();
54833     },
54834
54835     setContent : function(content, loadScripts){
54836         this.resizeEl.update(content, loadScripts);
54837     }
54838
54839 });
54840
54841
54842
54843
54844
54845
54846
54847
54848
54849 /**
54850  * @class Roo.TreePanel
54851  * @extends Roo.ContentPanel
54852  * @constructor
54853  * Create a new TreePanel. - defaults to fit/scoll contents.
54854  * @param {String/Object} config A string to set only the panel's title, or a config object
54855  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54856  */
54857 Roo.TreePanel = function(config){
54858     var el = config.el;
54859     var tree = config.tree;
54860     delete config.tree; 
54861     delete config.el; // hopefull!
54862     
54863     // wrapper for IE7 strict & safari scroll issue
54864     
54865     var treeEl = el.createChild();
54866     config.resizeEl = treeEl;
54867     
54868     
54869     
54870     Roo.TreePanel.superclass.constructor.call(this, el, config);
54871  
54872  
54873     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54874     //console.log(tree);
54875     this.on('activate', function()
54876     {
54877         if (this.tree.rendered) {
54878             return;
54879         }
54880         //console.log('render tree');
54881         this.tree.render();
54882     });
54883     // this should not be needed.. - it's actually the 'el' that resizes?
54884     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54885     
54886     //this.on('resize',  function (cp, w, h) {
54887     //        this.tree.innerCt.setWidth(w);
54888     //        this.tree.innerCt.setHeight(h);
54889     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54890     //});
54891
54892         
54893     
54894 };
54895
54896 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54897     fitToFrame : true,
54898     autoScroll : true
54899 });
54900
54901
54902
54903
54904
54905
54906
54907
54908
54909
54910
54911 /*
54912  * Based on:
54913  * Ext JS Library 1.1.1
54914  * Copyright(c) 2006-2007, Ext JS, LLC.
54915  *
54916  * Originally Released Under LGPL - original licence link has changed is not relivant.
54917  *
54918  * Fork - LGPL
54919  * <script type="text/javascript">
54920  */
54921  
54922
54923 /**
54924  * @class Roo.ReaderLayout
54925  * @extends Roo.BorderLayout
54926  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54927  * center region containing two nested regions (a top one for a list view and one for item preview below),
54928  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54929  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54930  * expedites the setup of the overall layout and regions for this common application style.
54931  * Example:
54932  <pre><code>
54933 var reader = new Roo.ReaderLayout();
54934 var CP = Roo.ContentPanel;  // shortcut for adding
54935
54936 reader.beginUpdate();
54937 reader.add("north", new CP("north", "North"));
54938 reader.add("west", new CP("west", {title: "West"}));
54939 reader.add("east", new CP("east", {title: "East"}));
54940
54941 reader.regions.listView.add(new CP("listView", "List"));
54942 reader.regions.preview.add(new CP("preview", "Preview"));
54943 reader.endUpdate();
54944 </code></pre>
54945 * @constructor
54946 * Create a new ReaderLayout
54947 * @param {Object} config Configuration options
54948 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54949 * document.body if omitted)
54950 */
54951 Roo.ReaderLayout = function(config, renderTo){
54952     var c = config || {size:{}};
54953     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54954         north: c.north !== false ? Roo.apply({
54955             split:false,
54956             initialSize: 32,
54957             titlebar: false
54958         }, c.north) : false,
54959         west: c.west !== false ? Roo.apply({
54960             split:true,
54961             initialSize: 200,
54962             minSize: 175,
54963             maxSize: 400,
54964             titlebar: true,
54965             collapsible: true,
54966             animate: true,
54967             margins:{left:5,right:0,bottom:5,top:5},
54968             cmargins:{left:5,right:5,bottom:5,top:5}
54969         }, c.west) : false,
54970         east: c.east !== false ? Roo.apply({
54971             split:true,
54972             initialSize: 200,
54973             minSize: 175,
54974             maxSize: 400,
54975             titlebar: true,
54976             collapsible: true,
54977             animate: true,
54978             margins:{left:0,right:5,bottom:5,top:5},
54979             cmargins:{left:5,right:5,bottom:5,top:5}
54980         }, c.east) : false,
54981         center: Roo.apply({
54982             tabPosition: 'top',
54983             autoScroll:false,
54984             closeOnTab: true,
54985             titlebar:false,
54986             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54987         }, c.center)
54988     });
54989
54990     this.el.addClass('x-reader');
54991
54992     this.beginUpdate();
54993
54994     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54995         south: c.preview !== false ? Roo.apply({
54996             split:true,
54997             initialSize: 200,
54998             minSize: 100,
54999             autoScroll:true,
55000             collapsible:true,
55001             titlebar: true,
55002             cmargins:{top:5,left:0, right:0, bottom:0}
55003         }, c.preview) : false,
55004         center: Roo.apply({
55005             autoScroll:false,
55006             titlebar:false,
55007             minHeight:200
55008         }, c.listView)
55009     });
55010     this.add('center', new Roo.NestedLayoutPanel(inner,
55011             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55012
55013     this.endUpdate();
55014
55015     this.regions.preview = inner.getRegion('south');
55016     this.regions.listView = inner.getRegion('center');
55017 };
55018
55019 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55020  * Based on:
55021  * Ext JS Library 1.1.1
55022  * Copyright(c) 2006-2007, Ext JS, LLC.
55023  *
55024  * Originally Released Under LGPL - original licence link has changed is not relivant.
55025  *
55026  * Fork - LGPL
55027  * <script type="text/javascript">
55028  */
55029  
55030 /**
55031  * @class Roo.grid.Grid
55032  * @extends Roo.util.Observable
55033  * This class represents the primary interface of a component based grid control.
55034  * <br><br>Usage:<pre><code>
55035  var grid = new Roo.grid.Grid("my-container-id", {
55036      ds: myDataStore,
55037      cm: myColModel,
55038      selModel: mySelectionModel,
55039      autoSizeColumns: true,
55040      monitorWindowResize: false,
55041      trackMouseOver: true
55042  });
55043  // set any options
55044  grid.render();
55045  * </code></pre>
55046  * <b>Common Problems:</b><br/>
55047  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55048  * element will correct this<br/>
55049  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55050  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55051  * are unpredictable.<br/>
55052  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55053  * grid to calculate dimensions/offsets.<br/>
55054   * @constructor
55055  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55056  * The container MUST have some type of size defined for the grid to fill. The container will be
55057  * automatically set to position relative if it isn't already.
55058  * @param {Object} config A config object that sets properties on this grid.
55059  */
55060 Roo.grid.Grid = function(container, config){
55061         // initialize the container
55062         this.container = Roo.get(container);
55063         this.container.update("");
55064         this.container.setStyle("overflow", "hidden");
55065     this.container.addClass('x-grid-container');
55066
55067     this.id = this.container.id;
55068
55069     Roo.apply(this, config);
55070     // check and correct shorthanded configs
55071     if(this.ds){
55072         this.dataSource = this.ds;
55073         delete this.ds;
55074     }
55075     if(this.cm){
55076         this.colModel = this.cm;
55077         delete this.cm;
55078     }
55079     if(this.sm){
55080         this.selModel = this.sm;
55081         delete this.sm;
55082     }
55083
55084     if (this.selModel) {
55085         this.selModel = Roo.factory(this.selModel, Roo.grid);
55086         this.sm = this.selModel;
55087         this.sm.xmodule = this.xmodule || false;
55088     }
55089     if (typeof(this.colModel.config) == 'undefined') {
55090         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55091         this.cm = this.colModel;
55092         this.cm.xmodule = this.xmodule || false;
55093     }
55094     if (this.dataSource) {
55095         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55096         this.ds = this.dataSource;
55097         this.ds.xmodule = this.xmodule || false;
55098          
55099     }
55100     
55101     
55102     
55103     if(this.width){
55104         this.container.setWidth(this.width);
55105     }
55106
55107     if(this.height){
55108         this.container.setHeight(this.height);
55109     }
55110     /** @private */
55111         this.addEvents({
55112         // raw events
55113         /**
55114          * @event click
55115          * The raw click event for the entire grid.
55116          * @param {Roo.EventObject} e
55117          */
55118         "click" : true,
55119         /**
55120          * @event dblclick
55121          * The raw dblclick event for the entire grid.
55122          * @param {Roo.EventObject} e
55123          */
55124         "dblclick" : true,
55125         /**
55126          * @event contextmenu
55127          * The raw contextmenu event for the entire grid.
55128          * @param {Roo.EventObject} e
55129          */
55130         "contextmenu" : true,
55131         /**
55132          * @event mousedown
55133          * The raw mousedown event for the entire grid.
55134          * @param {Roo.EventObject} e
55135          */
55136         "mousedown" : true,
55137         /**
55138          * @event mouseup
55139          * The raw mouseup event for the entire grid.
55140          * @param {Roo.EventObject} e
55141          */
55142         "mouseup" : true,
55143         /**
55144          * @event mouseover
55145          * The raw mouseover event for the entire grid.
55146          * @param {Roo.EventObject} e
55147          */
55148         "mouseover" : true,
55149         /**
55150          * @event mouseout
55151          * The raw mouseout event for the entire grid.
55152          * @param {Roo.EventObject} e
55153          */
55154         "mouseout" : true,
55155         /**
55156          * @event keypress
55157          * The raw keypress event for the entire grid.
55158          * @param {Roo.EventObject} e
55159          */
55160         "keypress" : true,
55161         /**
55162          * @event keydown
55163          * The raw keydown event for the entire grid.
55164          * @param {Roo.EventObject} e
55165          */
55166         "keydown" : true,
55167
55168         // custom events
55169
55170         /**
55171          * @event cellclick
55172          * Fires when a cell is clicked
55173          * @param {Grid} this
55174          * @param {Number} rowIndex
55175          * @param {Number} columnIndex
55176          * @param {Roo.EventObject} e
55177          */
55178         "cellclick" : true,
55179         /**
55180          * @event celldblclick
55181          * Fires when a cell is double clicked
55182          * @param {Grid} this
55183          * @param {Number} rowIndex
55184          * @param {Number} columnIndex
55185          * @param {Roo.EventObject} e
55186          */
55187         "celldblclick" : true,
55188         /**
55189          * @event rowclick
55190          * Fires when a row is clicked
55191          * @param {Grid} this
55192          * @param {Number} rowIndex
55193          * @param {Roo.EventObject} e
55194          */
55195         "rowclick" : true,
55196         /**
55197          * @event rowdblclick
55198          * Fires when a row is double clicked
55199          * @param {Grid} this
55200          * @param {Number} rowIndex
55201          * @param {Roo.EventObject} e
55202          */
55203         "rowdblclick" : true,
55204         /**
55205          * @event headerclick
55206          * Fires when a header is clicked
55207          * @param {Grid} this
55208          * @param {Number} columnIndex
55209          * @param {Roo.EventObject} e
55210          */
55211         "headerclick" : true,
55212         /**
55213          * @event headerdblclick
55214          * Fires when a header cell is double clicked
55215          * @param {Grid} this
55216          * @param {Number} columnIndex
55217          * @param {Roo.EventObject} e
55218          */
55219         "headerdblclick" : true,
55220         /**
55221          * @event rowcontextmenu
55222          * Fires when a row is right clicked
55223          * @param {Grid} this
55224          * @param {Number} rowIndex
55225          * @param {Roo.EventObject} e
55226          */
55227         "rowcontextmenu" : true,
55228         /**
55229          * @event cellcontextmenu
55230          * Fires when a cell is right clicked
55231          * @param {Grid} this
55232          * @param {Number} rowIndex
55233          * @param {Number} cellIndex
55234          * @param {Roo.EventObject} e
55235          */
55236          "cellcontextmenu" : true,
55237         /**
55238          * @event headercontextmenu
55239          * Fires when a header is right clicked
55240          * @param {Grid} this
55241          * @param {Number} columnIndex
55242          * @param {Roo.EventObject} e
55243          */
55244         "headercontextmenu" : true,
55245         /**
55246          * @event bodyscroll
55247          * Fires when the body element is scrolled
55248          * @param {Number} scrollLeft
55249          * @param {Number} scrollTop
55250          */
55251         "bodyscroll" : true,
55252         /**
55253          * @event columnresize
55254          * Fires when the user resizes a column
55255          * @param {Number} columnIndex
55256          * @param {Number} newSize
55257          */
55258         "columnresize" : true,
55259         /**
55260          * @event columnmove
55261          * Fires when the user moves a column
55262          * @param {Number} oldIndex
55263          * @param {Number} newIndex
55264          */
55265         "columnmove" : true,
55266         /**
55267          * @event startdrag
55268          * Fires when row(s) start being dragged
55269          * @param {Grid} this
55270          * @param {Roo.GridDD} dd The drag drop object
55271          * @param {event} e The raw browser event
55272          */
55273         "startdrag" : true,
55274         /**
55275          * @event enddrag
55276          * Fires when a drag operation is complete
55277          * @param {Grid} this
55278          * @param {Roo.GridDD} dd The drag drop object
55279          * @param {event} e The raw browser event
55280          */
55281         "enddrag" : true,
55282         /**
55283          * @event dragdrop
55284          * Fires when dragged row(s) are dropped on a valid DD target
55285          * @param {Grid} this
55286          * @param {Roo.GridDD} dd The drag drop object
55287          * @param {String} targetId The target drag drop object
55288          * @param {event} e The raw browser event
55289          */
55290         "dragdrop" : true,
55291         /**
55292          * @event dragover
55293          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55294          * @param {Grid} this
55295          * @param {Roo.GridDD} dd The drag drop object
55296          * @param {String} targetId The target drag drop object
55297          * @param {event} e The raw browser event
55298          */
55299         "dragover" : true,
55300         /**
55301          * @event dragenter
55302          *  Fires when the dragged row(s) first cross another DD target while being dragged
55303          * @param {Grid} this
55304          * @param {Roo.GridDD} dd The drag drop object
55305          * @param {String} targetId The target drag drop object
55306          * @param {event} e The raw browser event
55307          */
55308         "dragenter" : true,
55309         /**
55310          * @event dragout
55311          * Fires when the dragged row(s) leave another DD target while being dragged
55312          * @param {Grid} this
55313          * @param {Roo.GridDD} dd The drag drop object
55314          * @param {String} targetId The target drag drop object
55315          * @param {event} e The raw browser event
55316          */
55317         "dragout" : true,
55318         /**
55319          * @event rowclass
55320          * Fires when a row is rendered, so you can change add a style to it.
55321          * @param {GridView} gridview   The grid view
55322          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55323          */
55324         'rowclass' : true,
55325
55326         /**
55327          * @event render
55328          * Fires when the grid is rendered
55329          * @param {Grid} grid
55330          */
55331         'render' : true
55332     });
55333
55334     Roo.grid.Grid.superclass.constructor.call(this);
55335 };
55336 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55337     
55338     /**
55339      * @cfg {String} ddGroup - drag drop group.
55340      */
55341
55342     /**
55343      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55344      */
55345     minColumnWidth : 25,
55346
55347     /**
55348      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55349      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55350      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55351      */
55352     autoSizeColumns : false,
55353
55354     /**
55355      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55356      */
55357     autoSizeHeaders : true,
55358
55359     /**
55360      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55361      */
55362     monitorWindowResize : true,
55363
55364     /**
55365      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55366      * rows measured to get a columns size. Default is 0 (all rows).
55367      */
55368     maxRowsToMeasure : 0,
55369
55370     /**
55371      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55372      */
55373     trackMouseOver : true,
55374
55375     /**
55376     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55377     */
55378     
55379     /**
55380     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55381     */
55382     enableDragDrop : false,
55383     
55384     /**
55385     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55386     */
55387     enableColumnMove : true,
55388     
55389     /**
55390     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55391     */
55392     enableColumnHide : true,
55393     
55394     /**
55395     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55396     */
55397     enableRowHeightSync : false,
55398     
55399     /**
55400     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55401     */
55402     stripeRows : true,
55403     
55404     /**
55405     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55406     */
55407     autoHeight : false,
55408
55409     /**
55410      * @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.
55411      */
55412     autoExpandColumn : false,
55413
55414     /**
55415     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55416     * Default is 50.
55417     */
55418     autoExpandMin : 50,
55419
55420     /**
55421     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55422     */
55423     autoExpandMax : 1000,
55424
55425     /**
55426     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55427     */
55428     view : null,
55429
55430     /**
55431     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55432     */
55433     loadMask : false,
55434     /**
55435     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55436     */
55437     dropTarget: false,
55438     
55439    
55440     
55441     // private
55442     rendered : false,
55443
55444     /**
55445     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55446     * of a fixed width. Default is false.
55447     */
55448     /**
55449     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55450     */
55451     /**
55452      * Called once after all setup has been completed and the grid is ready to be rendered.
55453      * @return {Roo.grid.Grid} this
55454      */
55455     render : function()
55456     {
55457         var c = this.container;
55458         // try to detect autoHeight/width mode
55459         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55460             this.autoHeight = true;
55461         }
55462         var view = this.getView();
55463         view.init(this);
55464
55465         c.on("click", this.onClick, this);
55466         c.on("dblclick", this.onDblClick, this);
55467         c.on("contextmenu", this.onContextMenu, this);
55468         c.on("keydown", this.onKeyDown, this);
55469         if (Roo.isTouch) {
55470             c.on("touchstart", this.onTouchStart, this);
55471         }
55472
55473         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55474
55475         this.getSelectionModel().init(this);
55476
55477         view.render();
55478
55479         if(this.loadMask){
55480             this.loadMask = new Roo.LoadMask(this.container,
55481                     Roo.apply({store:this.dataSource}, this.loadMask));
55482         }
55483         
55484         
55485         if (this.toolbar && this.toolbar.xtype) {
55486             this.toolbar.container = this.getView().getHeaderPanel(true);
55487             this.toolbar = new Roo.Toolbar(this.toolbar);
55488         }
55489         if (this.footer && this.footer.xtype) {
55490             this.footer.dataSource = this.getDataSource();
55491             this.footer.container = this.getView().getFooterPanel(true);
55492             this.footer = Roo.factory(this.footer, Roo);
55493         }
55494         if (this.dropTarget && this.dropTarget.xtype) {
55495             delete this.dropTarget.xtype;
55496             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55497         }
55498         
55499         
55500         this.rendered = true;
55501         this.fireEvent('render', this);
55502         return this;
55503     },
55504
55505     /**
55506      * Reconfigures the grid to use a different Store and Column Model.
55507      * The View will be bound to the new objects and refreshed.
55508      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55509      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55510      */
55511     reconfigure : function(dataSource, colModel){
55512         if(this.loadMask){
55513             this.loadMask.destroy();
55514             this.loadMask = new Roo.LoadMask(this.container,
55515                     Roo.apply({store:dataSource}, this.loadMask));
55516         }
55517         this.view.bind(dataSource, colModel);
55518         this.dataSource = dataSource;
55519         this.colModel = colModel;
55520         this.view.refresh(true);
55521     },
55522     /**
55523      * addColumns
55524      * Add's a column, default at the end..
55525      
55526      * @param {int} position to add (default end)
55527      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55528      */
55529     addColumns : function(pos, ar)
55530     {
55531         
55532         for (var i =0;i< ar.length;i++) {
55533             var cfg = ar[i];
55534             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55535             this.cm.lookup[cfg.id] = cfg;
55536         }
55537         
55538         
55539         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55540             pos = this.cm.config.length; //this.cm.config.push(cfg);
55541         } 
55542         pos = Math.max(0,pos);
55543         ar.unshift(0);
55544         ar.unshift(pos);
55545         this.cm.config.splice.apply(this.cm.config, ar);
55546         
55547         
55548         
55549         this.view.generateRules(this.cm);
55550         this.view.refresh(true);
55551         
55552     },
55553     
55554     
55555     
55556     
55557     // private
55558     onKeyDown : function(e){
55559         this.fireEvent("keydown", e);
55560     },
55561
55562     /**
55563      * Destroy this grid.
55564      * @param {Boolean} removeEl True to remove the element
55565      */
55566     destroy : function(removeEl, keepListeners){
55567         if(this.loadMask){
55568             this.loadMask.destroy();
55569         }
55570         var c = this.container;
55571         c.removeAllListeners();
55572         this.view.destroy();
55573         this.colModel.purgeListeners();
55574         if(!keepListeners){
55575             this.purgeListeners();
55576         }
55577         c.update("");
55578         if(removeEl === true){
55579             c.remove();
55580         }
55581     },
55582
55583     // private
55584     processEvent : function(name, e){
55585         // does this fire select???
55586         //Roo.log('grid:processEvent '  + name);
55587         
55588         if (name != 'touchstart' ) {
55589             this.fireEvent(name, e);    
55590         }
55591         
55592         var t = e.getTarget();
55593         var v = this.view;
55594         var header = v.findHeaderIndex(t);
55595         if(header !== false){
55596             var ename = name == 'touchstart' ? 'click' : name;
55597              
55598             this.fireEvent("header" + ename, this, header, e);
55599         }else{
55600             var row = v.findRowIndex(t);
55601             var cell = v.findCellIndex(t);
55602             if (name == 'touchstart') {
55603                 // first touch is always a click.
55604                 // hopefull this happens after selection is updated.?
55605                 name = false;
55606                 
55607                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55608                     var cs = this.selModel.getSelectedCell();
55609                     if (row == cs[0] && cell == cs[1]){
55610                         name = 'dblclick';
55611                     }
55612                 }
55613                 if (typeof(this.selModel.getSelections) != 'undefined') {
55614                     var cs = this.selModel.getSelections();
55615                     var ds = this.dataSource;
55616                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55617                         name = 'dblclick';
55618                     }
55619                 }
55620                 if (!name) {
55621                     return;
55622                 }
55623             }
55624             
55625             
55626             if(row !== false){
55627                 this.fireEvent("row" + name, this, row, e);
55628                 if(cell !== false){
55629                     this.fireEvent("cell" + name, this, row, cell, e);
55630                 }
55631             }
55632         }
55633     },
55634
55635     // private
55636     onClick : function(e){
55637         this.processEvent("click", e);
55638     },
55639    // private
55640     onTouchStart : function(e){
55641         this.processEvent("touchstart", e);
55642     },
55643
55644     // private
55645     onContextMenu : function(e, t){
55646         this.processEvent("contextmenu", e);
55647     },
55648
55649     // private
55650     onDblClick : function(e){
55651         this.processEvent("dblclick", e);
55652     },
55653
55654     // private
55655     walkCells : function(row, col, step, fn, scope){
55656         var cm = this.colModel, clen = cm.getColumnCount();
55657         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55658         if(step < 0){
55659             if(col < 0){
55660                 row--;
55661                 first = false;
55662             }
55663             while(row >= 0){
55664                 if(!first){
55665                     col = clen-1;
55666                 }
55667                 first = false;
55668                 while(col >= 0){
55669                     if(fn.call(scope || this, row, col, cm) === true){
55670                         return [row, col];
55671                     }
55672                     col--;
55673                 }
55674                 row--;
55675             }
55676         } else {
55677             if(col >= clen){
55678                 row++;
55679                 first = false;
55680             }
55681             while(row < rlen){
55682                 if(!first){
55683                     col = 0;
55684                 }
55685                 first = false;
55686                 while(col < clen){
55687                     if(fn.call(scope || this, row, col, cm) === true){
55688                         return [row, col];
55689                     }
55690                     col++;
55691                 }
55692                 row++;
55693             }
55694         }
55695         return null;
55696     },
55697
55698     // private
55699     getSelections : function(){
55700         return this.selModel.getSelections();
55701     },
55702
55703     /**
55704      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55705      * but if manual update is required this method will initiate it.
55706      */
55707     autoSize : function(){
55708         if(this.rendered){
55709             this.view.layout();
55710             if(this.view.adjustForScroll){
55711                 this.view.adjustForScroll();
55712             }
55713         }
55714     },
55715
55716     /**
55717      * Returns the grid's underlying element.
55718      * @return {Element} The element
55719      */
55720     getGridEl : function(){
55721         return this.container;
55722     },
55723
55724     // private for compatibility, overridden by editor grid
55725     stopEditing : function(){},
55726
55727     /**
55728      * Returns the grid's SelectionModel.
55729      * @return {SelectionModel}
55730      */
55731     getSelectionModel : function(){
55732         if(!this.selModel){
55733             this.selModel = new Roo.grid.RowSelectionModel();
55734         }
55735         return this.selModel;
55736     },
55737
55738     /**
55739      * Returns the grid's DataSource.
55740      * @return {DataSource}
55741      */
55742     getDataSource : function(){
55743         return this.dataSource;
55744     },
55745
55746     /**
55747      * Returns the grid's ColumnModel.
55748      * @return {ColumnModel}
55749      */
55750     getColumnModel : function(){
55751         return this.colModel;
55752     },
55753
55754     /**
55755      * Returns the grid's GridView object.
55756      * @return {GridView}
55757      */
55758     getView : function(){
55759         if(!this.view){
55760             this.view = new Roo.grid.GridView(this.viewConfig);
55761         }
55762         return this.view;
55763     },
55764     /**
55765      * Called to get grid's drag proxy text, by default returns this.ddText.
55766      * @return {String}
55767      */
55768     getDragDropText : function(){
55769         var count = this.selModel.getCount();
55770         return String.format(this.ddText, count, count == 1 ? '' : 's');
55771     }
55772 });
55773 /**
55774  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55775  * %0 is replaced with the number of selected rows.
55776  * @type String
55777  */
55778 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55779  * Based on:
55780  * Ext JS Library 1.1.1
55781  * Copyright(c) 2006-2007, Ext JS, LLC.
55782  *
55783  * Originally Released Under LGPL - original licence link has changed is not relivant.
55784  *
55785  * Fork - LGPL
55786  * <script type="text/javascript">
55787  */
55788  
55789 Roo.grid.AbstractGridView = function(){
55790         this.grid = null;
55791         
55792         this.events = {
55793             "beforerowremoved" : true,
55794             "beforerowsinserted" : true,
55795             "beforerefresh" : true,
55796             "rowremoved" : true,
55797             "rowsinserted" : true,
55798             "rowupdated" : true,
55799             "refresh" : true
55800         };
55801     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55802 };
55803
55804 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55805     rowClass : "x-grid-row",
55806     cellClass : "x-grid-cell",
55807     tdClass : "x-grid-td",
55808     hdClass : "x-grid-hd",
55809     splitClass : "x-grid-hd-split",
55810     
55811     init: function(grid){
55812         this.grid = grid;
55813                 var cid = this.grid.getGridEl().id;
55814         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55815         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55816         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55817         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55818         },
55819         
55820     getColumnRenderers : function(){
55821         var renderers = [];
55822         var cm = this.grid.colModel;
55823         var colCount = cm.getColumnCount();
55824         for(var i = 0; i < colCount; i++){
55825             renderers[i] = cm.getRenderer(i);
55826         }
55827         return renderers;
55828     },
55829     
55830     getColumnIds : function(){
55831         var ids = [];
55832         var cm = this.grid.colModel;
55833         var colCount = cm.getColumnCount();
55834         for(var i = 0; i < colCount; i++){
55835             ids[i] = cm.getColumnId(i);
55836         }
55837         return ids;
55838     },
55839     
55840     getDataIndexes : function(){
55841         if(!this.indexMap){
55842             this.indexMap = this.buildIndexMap();
55843         }
55844         return this.indexMap.colToData;
55845     },
55846     
55847     getColumnIndexByDataIndex : function(dataIndex){
55848         if(!this.indexMap){
55849             this.indexMap = this.buildIndexMap();
55850         }
55851         return this.indexMap.dataToCol[dataIndex];
55852     },
55853     
55854     /**
55855      * Set a css style for a column dynamically. 
55856      * @param {Number} colIndex The index of the column
55857      * @param {String} name The css property name
55858      * @param {String} value The css value
55859      */
55860     setCSSStyle : function(colIndex, name, value){
55861         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55862         Roo.util.CSS.updateRule(selector, name, value);
55863     },
55864     
55865     generateRules : function(cm){
55866         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55867         Roo.util.CSS.removeStyleSheet(rulesId);
55868         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55869             var cid = cm.getColumnId(i);
55870             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55871                          this.tdSelector, cid, " {\n}\n",
55872                          this.hdSelector, cid, " {\n}\n",
55873                          this.splitSelector, cid, " {\n}\n");
55874         }
55875         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55876     }
55877 });/*
55878  * Based on:
55879  * Ext JS Library 1.1.1
55880  * Copyright(c) 2006-2007, Ext JS, LLC.
55881  *
55882  * Originally Released Under LGPL - original licence link has changed is not relivant.
55883  *
55884  * Fork - LGPL
55885  * <script type="text/javascript">
55886  */
55887
55888 // private
55889 // This is a support class used internally by the Grid components
55890 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55891     this.grid = grid;
55892     this.view = grid.getView();
55893     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55894     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55895     if(hd2){
55896         this.setHandleElId(Roo.id(hd));
55897         this.setOuterHandleElId(Roo.id(hd2));
55898     }
55899     this.scroll = false;
55900 };
55901 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55902     maxDragWidth: 120,
55903     getDragData : function(e){
55904         var t = Roo.lib.Event.getTarget(e);
55905         var h = this.view.findHeaderCell(t);
55906         if(h){
55907             return {ddel: h.firstChild, header:h};
55908         }
55909         return false;
55910     },
55911
55912     onInitDrag : function(e){
55913         this.view.headersDisabled = true;
55914         var clone = this.dragData.ddel.cloneNode(true);
55915         clone.id = Roo.id();
55916         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55917         this.proxy.update(clone);
55918         return true;
55919     },
55920
55921     afterValidDrop : function(){
55922         var v = this.view;
55923         setTimeout(function(){
55924             v.headersDisabled = false;
55925         }, 50);
55926     },
55927
55928     afterInvalidDrop : function(){
55929         var v = this.view;
55930         setTimeout(function(){
55931             v.headersDisabled = false;
55932         }, 50);
55933     }
55934 });
55935 /*
55936  * Based on:
55937  * Ext JS Library 1.1.1
55938  * Copyright(c) 2006-2007, Ext JS, LLC.
55939  *
55940  * Originally Released Under LGPL - original licence link has changed is not relivant.
55941  *
55942  * Fork - LGPL
55943  * <script type="text/javascript">
55944  */
55945 // private
55946 // This is a support class used internally by the Grid components
55947 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55948     this.grid = grid;
55949     this.view = grid.getView();
55950     // split the proxies so they don't interfere with mouse events
55951     this.proxyTop = Roo.DomHelper.append(document.body, {
55952         cls:"col-move-top", html:"&#160;"
55953     }, true);
55954     this.proxyBottom = Roo.DomHelper.append(document.body, {
55955         cls:"col-move-bottom", html:"&#160;"
55956     }, true);
55957     this.proxyTop.hide = this.proxyBottom.hide = function(){
55958         this.setLeftTop(-100,-100);
55959         this.setStyle("visibility", "hidden");
55960     };
55961     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55962     // temporarily disabled
55963     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55964     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55965 };
55966 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55967     proxyOffsets : [-4, -9],
55968     fly: Roo.Element.fly,
55969
55970     getTargetFromEvent : function(e){
55971         var t = Roo.lib.Event.getTarget(e);
55972         var cindex = this.view.findCellIndex(t);
55973         if(cindex !== false){
55974             return this.view.getHeaderCell(cindex);
55975         }
55976         return null;
55977     },
55978
55979     nextVisible : function(h){
55980         var v = this.view, cm = this.grid.colModel;
55981         h = h.nextSibling;
55982         while(h){
55983             if(!cm.isHidden(v.getCellIndex(h))){
55984                 return h;
55985             }
55986             h = h.nextSibling;
55987         }
55988         return null;
55989     },
55990
55991     prevVisible : function(h){
55992         var v = this.view, cm = this.grid.colModel;
55993         h = h.prevSibling;
55994         while(h){
55995             if(!cm.isHidden(v.getCellIndex(h))){
55996                 return h;
55997             }
55998             h = h.prevSibling;
55999         }
56000         return null;
56001     },
56002
56003     positionIndicator : function(h, n, e){
56004         var x = Roo.lib.Event.getPageX(e);
56005         var r = Roo.lib.Dom.getRegion(n.firstChild);
56006         var px, pt, py = r.top + this.proxyOffsets[1];
56007         if((r.right - x) <= (r.right-r.left)/2){
56008             px = r.right+this.view.borderWidth;
56009             pt = "after";
56010         }else{
56011             px = r.left;
56012             pt = "before";
56013         }
56014         var oldIndex = this.view.getCellIndex(h);
56015         var newIndex = this.view.getCellIndex(n);
56016
56017         if(this.grid.colModel.isFixed(newIndex)){
56018             return false;
56019         }
56020
56021         var locked = this.grid.colModel.isLocked(newIndex);
56022
56023         if(pt == "after"){
56024             newIndex++;
56025         }
56026         if(oldIndex < newIndex){
56027             newIndex--;
56028         }
56029         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56030             return false;
56031         }
56032         px +=  this.proxyOffsets[0];
56033         this.proxyTop.setLeftTop(px, py);
56034         this.proxyTop.show();
56035         if(!this.bottomOffset){
56036             this.bottomOffset = this.view.mainHd.getHeight();
56037         }
56038         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56039         this.proxyBottom.show();
56040         return pt;
56041     },
56042
56043     onNodeEnter : function(n, dd, e, data){
56044         if(data.header != n){
56045             this.positionIndicator(data.header, n, e);
56046         }
56047     },
56048
56049     onNodeOver : function(n, dd, e, data){
56050         var result = false;
56051         if(data.header != n){
56052             result = this.positionIndicator(data.header, n, e);
56053         }
56054         if(!result){
56055             this.proxyTop.hide();
56056             this.proxyBottom.hide();
56057         }
56058         return result ? this.dropAllowed : this.dropNotAllowed;
56059     },
56060
56061     onNodeOut : function(n, dd, e, data){
56062         this.proxyTop.hide();
56063         this.proxyBottom.hide();
56064     },
56065
56066     onNodeDrop : function(n, dd, e, data){
56067         var h = data.header;
56068         if(h != n){
56069             var cm = this.grid.colModel;
56070             var x = Roo.lib.Event.getPageX(e);
56071             var r = Roo.lib.Dom.getRegion(n.firstChild);
56072             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56073             var oldIndex = this.view.getCellIndex(h);
56074             var newIndex = this.view.getCellIndex(n);
56075             var locked = cm.isLocked(newIndex);
56076             if(pt == "after"){
56077                 newIndex++;
56078             }
56079             if(oldIndex < newIndex){
56080                 newIndex--;
56081             }
56082             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56083                 return false;
56084             }
56085             cm.setLocked(oldIndex, locked, true);
56086             cm.moveColumn(oldIndex, newIndex);
56087             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56088             return true;
56089         }
56090         return false;
56091     }
56092 });
56093 /*
56094  * Based on:
56095  * Ext JS Library 1.1.1
56096  * Copyright(c) 2006-2007, Ext JS, LLC.
56097  *
56098  * Originally Released Under LGPL - original licence link has changed is not relivant.
56099  *
56100  * Fork - LGPL
56101  * <script type="text/javascript">
56102  */
56103   
56104 /**
56105  * @class Roo.grid.GridView
56106  * @extends Roo.util.Observable
56107  *
56108  * @constructor
56109  * @param {Object} config
56110  */
56111 Roo.grid.GridView = function(config){
56112     Roo.grid.GridView.superclass.constructor.call(this);
56113     this.el = null;
56114
56115     Roo.apply(this, config);
56116 };
56117
56118 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56119
56120     unselectable :  'unselectable="on"',
56121     unselectableCls :  'x-unselectable',
56122     
56123     
56124     rowClass : "x-grid-row",
56125
56126     cellClass : "x-grid-col",
56127
56128     tdClass : "x-grid-td",
56129
56130     hdClass : "x-grid-hd",
56131
56132     splitClass : "x-grid-split",
56133
56134     sortClasses : ["sort-asc", "sort-desc"],
56135
56136     enableMoveAnim : false,
56137
56138     hlColor: "C3DAF9",
56139
56140     dh : Roo.DomHelper,
56141
56142     fly : Roo.Element.fly,
56143
56144     css : Roo.util.CSS,
56145
56146     borderWidth: 1,
56147
56148     splitOffset: 3,
56149
56150     scrollIncrement : 22,
56151
56152     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56153
56154     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56155
56156     bind : function(ds, cm){
56157         if(this.ds){
56158             this.ds.un("load", this.onLoad, this);
56159             this.ds.un("datachanged", this.onDataChange, this);
56160             this.ds.un("add", this.onAdd, this);
56161             this.ds.un("remove", this.onRemove, this);
56162             this.ds.un("update", this.onUpdate, this);
56163             this.ds.un("clear", this.onClear, this);
56164         }
56165         if(ds){
56166             ds.on("load", this.onLoad, this);
56167             ds.on("datachanged", this.onDataChange, this);
56168             ds.on("add", this.onAdd, this);
56169             ds.on("remove", this.onRemove, this);
56170             ds.on("update", this.onUpdate, this);
56171             ds.on("clear", this.onClear, this);
56172         }
56173         this.ds = ds;
56174
56175         if(this.cm){
56176             this.cm.un("widthchange", this.onColWidthChange, this);
56177             this.cm.un("headerchange", this.onHeaderChange, this);
56178             this.cm.un("hiddenchange", this.onHiddenChange, this);
56179             this.cm.un("columnmoved", this.onColumnMove, this);
56180             this.cm.un("columnlockchange", this.onColumnLock, this);
56181         }
56182         if(cm){
56183             this.generateRules(cm);
56184             cm.on("widthchange", this.onColWidthChange, this);
56185             cm.on("headerchange", this.onHeaderChange, this);
56186             cm.on("hiddenchange", this.onHiddenChange, this);
56187             cm.on("columnmoved", this.onColumnMove, this);
56188             cm.on("columnlockchange", this.onColumnLock, this);
56189         }
56190         this.cm = cm;
56191     },
56192
56193     init: function(grid){
56194         Roo.grid.GridView.superclass.init.call(this, grid);
56195
56196         this.bind(grid.dataSource, grid.colModel);
56197
56198         grid.on("headerclick", this.handleHeaderClick, this);
56199
56200         if(grid.trackMouseOver){
56201             grid.on("mouseover", this.onRowOver, this);
56202             grid.on("mouseout", this.onRowOut, this);
56203         }
56204         grid.cancelTextSelection = function(){};
56205         this.gridId = grid.id;
56206
56207         var tpls = this.templates || {};
56208
56209         if(!tpls.master){
56210             tpls.master = new Roo.Template(
56211                '<div class="x-grid" hidefocus="true">',
56212                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56213                   '<div class="x-grid-topbar"></div>',
56214                   '<div class="x-grid-scroller"><div></div></div>',
56215                   '<div class="x-grid-locked">',
56216                       '<div class="x-grid-header">{lockedHeader}</div>',
56217                       '<div class="x-grid-body">{lockedBody}</div>',
56218                   "</div>",
56219                   '<div class="x-grid-viewport">',
56220                       '<div class="x-grid-header">{header}</div>',
56221                       '<div class="x-grid-body">{body}</div>',
56222                   "</div>",
56223                   '<div class="x-grid-bottombar"></div>',
56224                  
56225                   '<div class="x-grid-resize-proxy">&#160;</div>',
56226                "</div>"
56227             );
56228             tpls.master.disableformats = true;
56229         }
56230
56231         if(!tpls.header){
56232             tpls.header = new Roo.Template(
56233                '<table border="0" cellspacing="0" cellpadding="0">',
56234                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56235                "</table>{splits}"
56236             );
56237             tpls.header.disableformats = true;
56238         }
56239         tpls.header.compile();
56240
56241         if(!tpls.hcell){
56242             tpls.hcell = new Roo.Template(
56243                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56244                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56245                 "</div></td>"
56246              );
56247              tpls.hcell.disableFormats = true;
56248         }
56249         tpls.hcell.compile();
56250
56251         if(!tpls.hsplit){
56252             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56253                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56254             tpls.hsplit.disableFormats = true;
56255         }
56256         tpls.hsplit.compile();
56257
56258         if(!tpls.body){
56259             tpls.body = new Roo.Template(
56260                '<table border="0" cellspacing="0" cellpadding="0">',
56261                "<tbody>{rows}</tbody>",
56262                "</table>"
56263             );
56264             tpls.body.disableFormats = true;
56265         }
56266         tpls.body.compile();
56267
56268         if(!tpls.row){
56269             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56270             tpls.row.disableFormats = true;
56271         }
56272         tpls.row.compile();
56273
56274         if(!tpls.cell){
56275             tpls.cell = new Roo.Template(
56276                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56277                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56278                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56279                 "</td>"
56280             );
56281             tpls.cell.disableFormats = true;
56282         }
56283         tpls.cell.compile();
56284
56285         this.templates = tpls;
56286     },
56287
56288     // remap these for backwards compat
56289     onColWidthChange : function(){
56290         this.updateColumns.apply(this, arguments);
56291     },
56292     onHeaderChange : function(){
56293         this.updateHeaders.apply(this, arguments);
56294     }, 
56295     onHiddenChange : function(){
56296         this.handleHiddenChange.apply(this, arguments);
56297     },
56298     onColumnMove : function(){
56299         this.handleColumnMove.apply(this, arguments);
56300     },
56301     onColumnLock : function(){
56302         this.handleLockChange.apply(this, arguments);
56303     },
56304
56305     onDataChange : function(){
56306         this.refresh();
56307         this.updateHeaderSortState();
56308     },
56309
56310     onClear : function(){
56311         this.refresh();
56312     },
56313
56314     onUpdate : function(ds, record){
56315         this.refreshRow(record);
56316     },
56317
56318     refreshRow : function(record){
56319         var ds = this.ds, index;
56320         if(typeof record == 'number'){
56321             index = record;
56322             record = ds.getAt(index);
56323         }else{
56324             index = ds.indexOf(record);
56325         }
56326         this.insertRows(ds, index, index, true);
56327         this.onRemove(ds, record, index+1, true);
56328         this.syncRowHeights(index, index);
56329         this.layout();
56330         this.fireEvent("rowupdated", this, index, record);
56331     },
56332
56333     onAdd : function(ds, records, index){
56334         this.insertRows(ds, index, index + (records.length-1));
56335     },
56336
56337     onRemove : function(ds, record, index, isUpdate){
56338         if(isUpdate !== true){
56339             this.fireEvent("beforerowremoved", this, index, record);
56340         }
56341         var bt = this.getBodyTable(), lt = this.getLockedTable();
56342         if(bt.rows[index]){
56343             bt.firstChild.removeChild(bt.rows[index]);
56344         }
56345         if(lt.rows[index]){
56346             lt.firstChild.removeChild(lt.rows[index]);
56347         }
56348         if(isUpdate !== true){
56349             this.stripeRows(index);
56350             this.syncRowHeights(index, index);
56351             this.layout();
56352             this.fireEvent("rowremoved", this, index, record);
56353         }
56354     },
56355
56356     onLoad : function(){
56357         this.scrollToTop();
56358     },
56359
56360     /**
56361      * Scrolls the grid to the top
56362      */
56363     scrollToTop : function(){
56364         if(this.scroller){
56365             this.scroller.dom.scrollTop = 0;
56366             this.syncScroll();
56367         }
56368     },
56369
56370     /**
56371      * Gets a panel in the header of the grid that can be used for toolbars etc.
56372      * After modifying the contents of this panel a call to grid.autoSize() may be
56373      * required to register any changes in size.
56374      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56375      * @return Roo.Element
56376      */
56377     getHeaderPanel : function(doShow){
56378         if(doShow){
56379             this.headerPanel.show();
56380         }
56381         return this.headerPanel;
56382     },
56383
56384     /**
56385      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56386      * After modifying the contents of this panel a call to grid.autoSize() may be
56387      * required to register any changes in size.
56388      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56389      * @return Roo.Element
56390      */
56391     getFooterPanel : function(doShow){
56392         if(doShow){
56393             this.footerPanel.show();
56394         }
56395         return this.footerPanel;
56396     },
56397
56398     initElements : function(){
56399         var E = Roo.Element;
56400         var el = this.grid.getGridEl().dom.firstChild;
56401         var cs = el.childNodes;
56402
56403         this.el = new E(el);
56404         
56405          this.focusEl = new E(el.firstChild);
56406         this.focusEl.swallowEvent("click", true);
56407         
56408         this.headerPanel = new E(cs[1]);
56409         this.headerPanel.enableDisplayMode("block");
56410
56411         this.scroller = new E(cs[2]);
56412         this.scrollSizer = new E(this.scroller.dom.firstChild);
56413
56414         this.lockedWrap = new E(cs[3]);
56415         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56416         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56417
56418         this.mainWrap = new E(cs[4]);
56419         this.mainHd = new E(this.mainWrap.dom.firstChild);
56420         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56421
56422         this.footerPanel = new E(cs[5]);
56423         this.footerPanel.enableDisplayMode("block");
56424
56425         this.resizeProxy = new E(cs[6]);
56426
56427         this.headerSelector = String.format(
56428            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56429            this.lockedHd.id, this.mainHd.id
56430         );
56431
56432         this.splitterSelector = String.format(
56433            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56434            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56435         );
56436     },
56437     idToCssName : function(s)
56438     {
56439         return s.replace(/[^a-z0-9]+/ig, '-');
56440     },
56441
56442     getHeaderCell : function(index){
56443         return Roo.DomQuery.select(this.headerSelector)[index];
56444     },
56445
56446     getHeaderCellMeasure : function(index){
56447         return this.getHeaderCell(index).firstChild;
56448     },
56449
56450     getHeaderCellText : function(index){
56451         return this.getHeaderCell(index).firstChild.firstChild;
56452     },
56453
56454     getLockedTable : function(){
56455         return this.lockedBody.dom.firstChild;
56456     },
56457
56458     getBodyTable : function(){
56459         return this.mainBody.dom.firstChild;
56460     },
56461
56462     getLockedRow : function(index){
56463         return this.getLockedTable().rows[index];
56464     },
56465
56466     getRow : function(index){
56467         return this.getBodyTable().rows[index];
56468     },
56469
56470     getRowComposite : function(index){
56471         if(!this.rowEl){
56472             this.rowEl = new Roo.CompositeElementLite();
56473         }
56474         var els = [], lrow, mrow;
56475         if(lrow = this.getLockedRow(index)){
56476             els.push(lrow);
56477         }
56478         if(mrow = this.getRow(index)){
56479             els.push(mrow);
56480         }
56481         this.rowEl.elements = els;
56482         return this.rowEl;
56483     },
56484     /**
56485      * Gets the 'td' of the cell
56486      * 
56487      * @param {Integer} rowIndex row to select
56488      * @param {Integer} colIndex column to select
56489      * 
56490      * @return {Object} 
56491      */
56492     getCell : function(rowIndex, colIndex){
56493         var locked = this.cm.getLockedCount();
56494         var source;
56495         if(colIndex < locked){
56496             source = this.lockedBody.dom.firstChild;
56497         }else{
56498             source = this.mainBody.dom.firstChild;
56499             colIndex -= locked;
56500         }
56501         return source.rows[rowIndex].childNodes[colIndex];
56502     },
56503
56504     getCellText : function(rowIndex, colIndex){
56505         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56506     },
56507
56508     getCellBox : function(cell){
56509         var b = this.fly(cell).getBox();
56510         if(Roo.isOpera){ // opera fails to report the Y
56511             b.y = cell.offsetTop + this.mainBody.getY();
56512         }
56513         return b;
56514     },
56515
56516     getCellIndex : function(cell){
56517         var id = String(cell.className).match(this.cellRE);
56518         if(id){
56519             return parseInt(id[1], 10);
56520         }
56521         return 0;
56522     },
56523
56524     findHeaderIndex : function(n){
56525         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56526         return r ? this.getCellIndex(r) : false;
56527     },
56528
56529     findHeaderCell : function(n){
56530         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56531         return r ? r : false;
56532     },
56533
56534     findRowIndex : function(n){
56535         if(!n){
56536             return false;
56537         }
56538         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56539         return r ? r.rowIndex : false;
56540     },
56541
56542     findCellIndex : function(node){
56543         var stop = this.el.dom;
56544         while(node && node != stop){
56545             if(this.findRE.test(node.className)){
56546                 return this.getCellIndex(node);
56547             }
56548             node = node.parentNode;
56549         }
56550         return false;
56551     },
56552
56553     getColumnId : function(index){
56554         return this.cm.getColumnId(index);
56555     },
56556
56557     getSplitters : function()
56558     {
56559         if(this.splitterSelector){
56560            return Roo.DomQuery.select(this.splitterSelector);
56561         }else{
56562             return null;
56563       }
56564     },
56565
56566     getSplitter : function(index){
56567         return this.getSplitters()[index];
56568     },
56569
56570     onRowOver : function(e, t){
56571         var row;
56572         if((row = this.findRowIndex(t)) !== false){
56573             this.getRowComposite(row).addClass("x-grid-row-over");
56574         }
56575     },
56576
56577     onRowOut : function(e, t){
56578         var row;
56579         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56580             this.getRowComposite(row).removeClass("x-grid-row-over");
56581         }
56582     },
56583
56584     renderHeaders : function(){
56585         var cm = this.cm;
56586         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56587         var cb = [], lb = [], sb = [], lsb = [], p = {};
56588         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56589             p.cellId = "x-grid-hd-0-" + i;
56590             p.splitId = "x-grid-csplit-0-" + i;
56591             p.id = cm.getColumnId(i);
56592             p.value = cm.getColumnHeader(i) || "";
56593             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56594             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56595             if(!cm.isLocked(i)){
56596                 cb[cb.length] = ct.apply(p);
56597                 sb[sb.length] = st.apply(p);
56598             }else{
56599                 lb[lb.length] = ct.apply(p);
56600                 lsb[lsb.length] = st.apply(p);
56601             }
56602         }
56603         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56604                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56605     },
56606
56607     updateHeaders : function(){
56608         var html = this.renderHeaders();
56609         this.lockedHd.update(html[0]);
56610         this.mainHd.update(html[1]);
56611     },
56612
56613     /**
56614      * Focuses the specified row.
56615      * @param {Number} row The row index
56616      */
56617     focusRow : function(row)
56618     {
56619         //Roo.log('GridView.focusRow');
56620         var x = this.scroller.dom.scrollLeft;
56621         this.focusCell(row, 0, false);
56622         this.scroller.dom.scrollLeft = x;
56623     },
56624
56625     /**
56626      * Focuses the specified cell.
56627      * @param {Number} row The row index
56628      * @param {Number} col The column index
56629      * @param {Boolean} hscroll false to disable horizontal scrolling
56630      */
56631     focusCell : function(row, col, hscroll)
56632     {
56633         //Roo.log('GridView.focusCell');
56634         var el = this.ensureVisible(row, col, hscroll);
56635         this.focusEl.alignTo(el, "tl-tl");
56636         if(Roo.isGecko){
56637             this.focusEl.focus();
56638         }else{
56639             this.focusEl.focus.defer(1, this.focusEl);
56640         }
56641     },
56642
56643     /**
56644      * Scrolls the specified cell into view
56645      * @param {Number} row The row index
56646      * @param {Number} col The column index
56647      * @param {Boolean} hscroll false to disable horizontal scrolling
56648      */
56649     ensureVisible : function(row, col, hscroll)
56650     {
56651         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56652         //return null; //disable for testing.
56653         if(typeof row != "number"){
56654             row = row.rowIndex;
56655         }
56656         if(row < 0 && row >= this.ds.getCount()){
56657             return  null;
56658         }
56659         col = (col !== undefined ? col : 0);
56660         var cm = this.grid.colModel;
56661         while(cm.isHidden(col)){
56662             col++;
56663         }
56664
56665         var el = this.getCell(row, col);
56666         if(!el){
56667             return null;
56668         }
56669         var c = this.scroller.dom;
56670
56671         var ctop = parseInt(el.offsetTop, 10);
56672         var cleft = parseInt(el.offsetLeft, 10);
56673         var cbot = ctop + el.offsetHeight;
56674         var cright = cleft + el.offsetWidth;
56675         
56676         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56677         var stop = parseInt(c.scrollTop, 10);
56678         var sleft = parseInt(c.scrollLeft, 10);
56679         var sbot = stop + ch;
56680         var sright = sleft + c.clientWidth;
56681         /*
56682         Roo.log('GridView.ensureVisible:' +
56683                 ' ctop:' + ctop +
56684                 ' c.clientHeight:' + c.clientHeight +
56685                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56686                 ' stop:' + stop +
56687                 ' cbot:' + cbot +
56688                 ' sbot:' + sbot +
56689                 ' ch:' + ch  
56690                 );
56691         */
56692         if(ctop < stop){
56693              c.scrollTop = ctop;
56694             //Roo.log("set scrolltop to ctop DISABLE?");
56695         }else if(cbot > sbot){
56696             //Roo.log("set scrolltop to cbot-ch");
56697             c.scrollTop = cbot-ch;
56698         }
56699         
56700         if(hscroll !== false){
56701             if(cleft < sleft){
56702                 c.scrollLeft = cleft;
56703             }else if(cright > sright){
56704                 c.scrollLeft = cright-c.clientWidth;
56705             }
56706         }
56707          
56708         return el;
56709     },
56710
56711     updateColumns : function(){
56712         this.grid.stopEditing();
56713         var cm = this.grid.colModel, colIds = this.getColumnIds();
56714         //var totalWidth = cm.getTotalWidth();
56715         var pos = 0;
56716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56717             //if(cm.isHidden(i)) continue;
56718             var w = cm.getColumnWidth(i);
56719             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56720             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56721         }
56722         this.updateSplitters();
56723     },
56724
56725     generateRules : function(cm){
56726         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56727         Roo.util.CSS.removeStyleSheet(rulesId);
56728         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56729             var cid = cm.getColumnId(i);
56730             var align = '';
56731             if(cm.config[i].align){
56732                 align = 'text-align:'+cm.config[i].align+';';
56733             }
56734             var hidden = '';
56735             if(cm.isHidden(i)){
56736                 hidden = 'display:none;';
56737             }
56738             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56739             ruleBuf.push(
56740                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56741                     this.hdSelector, cid, " {\n", align, width, "}\n",
56742                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56743                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56744         }
56745         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56746     },
56747
56748     updateSplitters : function(){
56749         var cm = this.cm, s = this.getSplitters();
56750         if(s){ // splitters not created yet
56751             var pos = 0, locked = true;
56752             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56753                 if(cm.isHidden(i)) {
56754                     continue;
56755                 }
56756                 var w = cm.getColumnWidth(i); // make sure it's a number
56757                 if(!cm.isLocked(i) && locked){
56758                     pos = 0;
56759                     locked = false;
56760                 }
56761                 pos += w;
56762                 s[i].style.left = (pos-this.splitOffset) + "px";
56763             }
56764         }
56765     },
56766
56767     handleHiddenChange : function(colModel, colIndex, hidden){
56768         if(hidden){
56769             this.hideColumn(colIndex);
56770         }else{
56771             this.unhideColumn(colIndex);
56772         }
56773     },
56774
56775     hideColumn : function(colIndex){
56776         var cid = this.getColumnId(colIndex);
56777         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56778         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56779         if(Roo.isSafari){
56780             this.updateHeaders();
56781         }
56782         this.updateSplitters();
56783         this.layout();
56784     },
56785
56786     unhideColumn : function(colIndex){
56787         var cid = this.getColumnId(colIndex);
56788         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56789         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56790
56791         if(Roo.isSafari){
56792             this.updateHeaders();
56793         }
56794         this.updateSplitters();
56795         this.layout();
56796     },
56797
56798     insertRows : function(dm, firstRow, lastRow, isUpdate){
56799         if(firstRow == 0 && lastRow == dm.getCount()-1){
56800             this.refresh();
56801         }else{
56802             if(!isUpdate){
56803                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56804             }
56805             var s = this.getScrollState();
56806             var markup = this.renderRows(firstRow, lastRow);
56807             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56808             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56809             this.restoreScroll(s);
56810             if(!isUpdate){
56811                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56812                 this.syncRowHeights(firstRow, lastRow);
56813                 this.stripeRows(firstRow);
56814                 this.layout();
56815             }
56816         }
56817     },
56818
56819     bufferRows : function(markup, target, index){
56820         var before = null, trows = target.rows, tbody = target.tBodies[0];
56821         if(index < trows.length){
56822             before = trows[index];
56823         }
56824         var b = document.createElement("div");
56825         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56826         var rows = b.firstChild.rows;
56827         for(var i = 0, len = rows.length; i < len; i++){
56828             if(before){
56829                 tbody.insertBefore(rows[0], before);
56830             }else{
56831                 tbody.appendChild(rows[0]);
56832             }
56833         }
56834         b.innerHTML = "";
56835         b = null;
56836     },
56837
56838     deleteRows : function(dm, firstRow, lastRow){
56839         if(dm.getRowCount()<1){
56840             this.fireEvent("beforerefresh", this);
56841             this.mainBody.update("");
56842             this.lockedBody.update("");
56843             this.fireEvent("refresh", this);
56844         }else{
56845             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56846             var bt = this.getBodyTable();
56847             var tbody = bt.firstChild;
56848             var rows = bt.rows;
56849             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56850                 tbody.removeChild(rows[firstRow]);
56851             }
56852             this.stripeRows(firstRow);
56853             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56854         }
56855     },
56856
56857     updateRows : function(dataSource, firstRow, lastRow){
56858         var s = this.getScrollState();
56859         this.refresh();
56860         this.restoreScroll(s);
56861     },
56862
56863     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56864         if(!noRefresh){
56865            this.refresh();
56866         }
56867         this.updateHeaderSortState();
56868     },
56869
56870     getScrollState : function(){
56871         
56872         var sb = this.scroller.dom;
56873         return {left: sb.scrollLeft, top: sb.scrollTop};
56874     },
56875
56876     stripeRows : function(startRow){
56877         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56878             return;
56879         }
56880         startRow = startRow || 0;
56881         var rows = this.getBodyTable().rows;
56882         var lrows = this.getLockedTable().rows;
56883         var cls = ' x-grid-row-alt ';
56884         for(var i = startRow, len = rows.length; i < len; i++){
56885             var row = rows[i], lrow = lrows[i];
56886             var isAlt = ((i+1) % 2 == 0);
56887             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56888             if(isAlt == hasAlt){
56889                 continue;
56890             }
56891             if(isAlt){
56892                 row.className += " x-grid-row-alt";
56893             }else{
56894                 row.className = row.className.replace("x-grid-row-alt", "");
56895             }
56896             if(lrow){
56897                 lrow.className = row.className;
56898             }
56899         }
56900     },
56901
56902     restoreScroll : function(state){
56903         //Roo.log('GridView.restoreScroll');
56904         var sb = this.scroller.dom;
56905         sb.scrollLeft = state.left;
56906         sb.scrollTop = state.top;
56907         this.syncScroll();
56908     },
56909
56910     syncScroll : function(){
56911         //Roo.log('GridView.syncScroll');
56912         var sb = this.scroller.dom;
56913         var sh = this.mainHd.dom;
56914         var bs = this.mainBody.dom;
56915         var lv = this.lockedBody.dom;
56916         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56917         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56918     },
56919
56920     handleScroll : function(e){
56921         this.syncScroll();
56922         var sb = this.scroller.dom;
56923         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56924         e.stopEvent();
56925     },
56926
56927     handleWheel : function(e){
56928         var d = e.getWheelDelta();
56929         this.scroller.dom.scrollTop -= d*22;
56930         // set this here to prevent jumpy scrolling on large tables
56931         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56932         e.stopEvent();
56933     },
56934
56935     renderRows : function(startRow, endRow){
56936         // pull in all the crap needed to render rows
56937         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56938         var colCount = cm.getColumnCount();
56939
56940         if(ds.getCount() < 1){
56941             return ["", ""];
56942         }
56943
56944         // build a map for all the columns
56945         var cs = [];
56946         for(var i = 0; i < colCount; i++){
56947             var name = cm.getDataIndex(i);
56948             cs[i] = {
56949                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56950                 renderer : cm.getRenderer(i),
56951                 id : cm.getColumnId(i),
56952                 locked : cm.isLocked(i),
56953                 has_editor : cm.isCellEditable(i)
56954             };
56955         }
56956
56957         startRow = startRow || 0;
56958         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56959
56960         // records to render
56961         var rs = ds.getRange(startRow, endRow);
56962
56963         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56964     },
56965
56966     // As much as I hate to duplicate code, this was branched because FireFox really hates
56967     // [].join("") on strings. The performance difference was substantial enough to
56968     // branch this function
56969     doRender : Roo.isGecko ?
56970             function(cs, rs, ds, startRow, colCount, stripe){
56971                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56972                 // buffers
56973                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56974                 
56975                 var hasListener = this.grid.hasListener('rowclass');
56976                 var rowcfg = {};
56977                 for(var j = 0, len = rs.length; j < len; j++){
56978                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56979                     for(var i = 0; i < colCount; i++){
56980                         c = cs[i];
56981                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56982                         p.id = c.id;
56983                         p.css = p.attr = "";
56984                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56985                         if(p.value == undefined || p.value === "") {
56986                             p.value = "&#160;";
56987                         }
56988                         if(c.has_editor){
56989                             p.css += ' x-grid-editable-cell';
56990                         }
56991                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56992                             p.css +=  ' x-grid-dirty-cell';
56993                         }
56994                         var markup = ct.apply(p);
56995                         if(!c.locked){
56996                             cb+= markup;
56997                         }else{
56998                             lcb+= markup;
56999                         }
57000                     }
57001                     var alt = [];
57002                     if(stripe && ((rowIndex+1) % 2 == 0)){
57003                         alt.push("x-grid-row-alt")
57004                     }
57005                     if(r.dirty){
57006                         alt.push(  " x-grid-dirty-row");
57007                     }
57008                     rp.cells = lcb;
57009                     if(this.getRowClass){
57010                         alt.push(this.getRowClass(r, rowIndex));
57011                     }
57012                     if (hasListener) {
57013                         rowcfg = {
57014                              
57015                             record: r,
57016                             rowIndex : rowIndex,
57017                             rowClass : ''
57018                         };
57019                         this.grid.fireEvent('rowclass', this, rowcfg);
57020                         alt.push(rowcfg.rowClass);
57021                     }
57022                     rp.alt = alt.join(" ");
57023                     lbuf+= rt.apply(rp);
57024                     rp.cells = cb;
57025                     buf+=  rt.apply(rp);
57026                 }
57027                 return [lbuf, buf];
57028             } :
57029             function(cs, rs, ds, startRow, colCount, stripe){
57030                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57031                 // buffers
57032                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57033                 var hasListener = this.grid.hasListener('rowclass');
57034  
57035                 var rowcfg = {};
57036                 for(var j = 0, len = rs.length; j < len; j++){
57037                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57038                     for(var i = 0; i < colCount; i++){
57039                         c = cs[i];
57040                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57041                         p.id = c.id;
57042                         p.css = p.attr = "";
57043                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57044                         if(p.value == undefined || p.value === "") {
57045                             p.value = "&#160;";
57046                         }
57047                         //Roo.log(c);
57048                          if(c.has_editor){
57049                             p.css += ' x-grid-editable-cell';
57050                         }
57051                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57052                             p.css += ' x-grid-dirty-cell' 
57053                         }
57054                         
57055                         var markup = ct.apply(p);
57056                         if(!c.locked){
57057                             cb[cb.length] = markup;
57058                         }else{
57059                             lcb[lcb.length] = markup;
57060                         }
57061                     }
57062                     var alt = [];
57063                     if(stripe && ((rowIndex+1) % 2 == 0)){
57064                         alt.push( "x-grid-row-alt");
57065                     }
57066                     if(r.dirty){
57067                         alt.push(" x-grid-dirty-row");
57068                     }
57069                     rp.cells = lcb;
57070                     if(this.getRowClass){
57071                         alt.push( this.getRowClass(r, rowIndex));
57072                     }
57073                     if (hasListener) {
57074                         rowcfg = {
57075                              
57076                             record: r,
57077                             rowIndex : rowIndex,
57078                             rowClass : ''
57079                         };
57080                         this.grid.fireEvent('rowclass', this, rowcfg);
57081                         alt.push(rowcfg.rowClass);
57082                     }
57083                     
57084                     rp.alt = alt.join(" ");
57085                     rp.cells = lcb.join("");
57086                     lbuf[lbuf.length] = rt.apply(rp);
57087                     rp.cells = cb.join("");
57088                     buf[buf.length] =  rt.apply(rp);
57089                 }
57090                 return [lbuf.join(""), buf.join("")];
57091             },
57092
57093     renderBody : function(){
57094         var markup = this.renderRows();
57095         var bt = this.templates.body;
57096         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57097     },
57098
57099     /**
57100      * Refreshes the grid
57101      * @param {Boolean} headersToo
57102      */
57103     refresh : function(headersToo){
57104         this.fireEvent("beforerefresh", this);
57105         this.grid.stopEditing();
57106         var result = this.renderBody();
57107         this.lockedBody.update(result[0]);
57108         this.mainBody.update(result[1]);
57109         if(headersToo === true){
57110             this.updateHeaders();
57111             this.updateColumns();
57112             this.updateSplitters();
57113             this.updateHeaderSortState();
57114         }
57115         this.syncRowHeights();
57116         this.layout();
57117         this.fireEvent("refresh", this);
57118     },
57119
57120     handleColumnMove : function(cm, oldIndex, newIndex){
57121         this.indexMap = null;
57122         var s = this.getScrollState();
57123         this.refresh(true);
57124         this.restoreScroll(s);
57125         this.afterMove(newIndex);
57126     },
57127
57128     afterMove : function(colIndex){
57129         if(this.enableMoveAnim && Roo.enableFx){
57130             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57131         }
57132         // if multisort - fix sortOrder, and reload..
57133         if (this.grid.dataSource.multiSort) {
57134             // the we can call sort again..
57135             var dm = this.grid.dataSource;
57136             var cm = this.grid.colModel;
57137             var so = [];
57138             for(var i = 0; i < cm.config.length; i++ ) {
57139                 
57140                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57141                     continue; // dont' bother, it's not in sort list or being set.
57142                 }
57143                 
57144                 so.push(cm.config[i].dataIndex);
57145             };
57146             dm.sortOrder = so;
57147             dm.load(dm.lastOptions);
57148             
57149             
57150         }
57151         
57152     },
57153
57154     updateCell : function(dm, rowIndex, dataIndex){
57155         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57156         if(typeof colIndex == "undefined"){ // not present in grid
57157             return;
57158         }
57159         var cm = this.grid.colModel;
57160         var cell = this.getCell(rowIndex, colIndex);
57161         var cellText = this.getCellText(rowIndex, colIndex);
57162
57163         var p = {
57164             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57165             id : cm.getColumnId(colIndex),
57166             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57167         };
57168         var renderer = cm.getRenderer(colIndex);
57169         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57170         if(typeof val == "undefined" || val === "") {
57171             val = "&#160;";
57172         }
57173         cellText.innerHTML = val;
57174         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57175         this.syncRowHeights(rowIndex, rowIndex);
57176     },
57177
57178     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57179         var maxWidth = 0;
57180         if(this.grid.autoSizeHeaders){
57181             var h = this.getHeaderCellMeasure(colIndex);
57182             maxWidth = Math.max(maxWidth, h.scrollWidth);
57183         }
57184         var tb, index;
57185         if(this.cm.isLocked(colIndex)){
57186             tb = this.getLockedTable();
57187             index = colIndex;
57188         }else{
57189             tb = this.getBodyTable();
57190             index = colIndex - this.cm.getLockedCount();
57191         }
57192         if(tb && tb.rows){
57193             var rows = tb.rows;
57194             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57195             for(var i = 0; i < stopIndex; i++){
57196                 var cell = rows[i].childNodes[index].firstChild;
57197                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57198             }
57199         }
57200         return maxWidth + /*margin for error in IE*/ 5;
57201     },
57202     /**
57203      * Autofit a column to its content.
57204      * @param {Number} colIndex
57205      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57206      */
57207      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57208          if(this.cm.isHidden(colIndex)){
57209              return; // can't calc a hidden column
57210          }
57211         if(forceMinSize){
57212             var cid = this.cm.getColumnId(colIndex);
57213             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57214            if(this.grid.autoSizeHeaders){
57215                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57216            }
57217         }
57218         var newWidth = this.calcColumnWidth(colIndex);
57219         this.cm.setColumnWidth(colIndex,
57220             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57221         if(!suppressEvent){
57222             this.grid.fireEvent("columnresize", colIndex, newWidth);
57223         }
57224     },
57225
57226     /**
57227      * Autofits all columns to their content and then expands to fit any extra space in the grid
57228      */
57229      autoSizeColumns : function(){
57230         var cm = this.grid.colModel;
57231         var colCount = cm.getColumnCount();
57232         for(var i = 0; i < colCount; i++){
57233             this.autoSizeColumn(i, true, true);
57234         }
57235         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57236             this.fitColumns();
57237         }else{
57238             this.updateColumns();
57239             this.layout();
57240         }
57241     },
57242
57243     /**
57244      * Autofits all columns to the grid's width proportionate with their current size
57245      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57246      */
57247     fitColumns : function(reserveScrollSpace){
57248         var cm = this.grid.colModel;
57249         var colCount = cm.getColumnCount();
57250         var cols = [];
57251         var width = 0;
57252         var i, w;
57253         for (i = 0; i < colCount; i++){
57254             if(!cm.isHidden(i) && !cm.isFixed(i)){
57255                 w = cm.getColumnWidth(i);
57256                 cols.push(i);
57257                 cols.push(w);
57258                 width += w;
57259             }
57260         }
57261         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57262         if(reserveScrollSpace){
57263             avail -= 17;
57264         }
57265         var frac = (avail - cm.getTotalWidth())/width;
57266         while (cols.length){
57267             w = cols.pop();
57268             i = cols.pop();
57269             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57270         }
57271         this.updateColumns();
57272         this.layout();
57273     },
57274
57275     onRowSelect : function(rowIndex){
57276         var row = this.getRowComposite(rowIndex);
57277         row.addClass("x-grid-row-selected");
57278     },
57279
57280     onRowDeselect : function(rowIndex){
57281         var row = this.getRowComposite(rowIndex);
57282         row.removeClass("x-grid-row-selected");
57283     },
57284
57285     onCellSelect : function(row, col){
57286         var cell = this.getCell(row, col);
57287         if(cell){
57288             Roo.fly(cell).addClass("x-grid-cell-selected");
57289         }
57290     },
57291
57292     onCellDeselect : function(row, col){
57293         var cell = this.getCell(row, col);
57294         if(cell){
57295             Roo.fly(cell).removeClass("x-grid-cell-selected");
57296         }
57297     },
57298
57299     updateHeaderSortState : function(){
57300         
57301         // sort state can be single { field: xxx, direction : yyy}
57302         // or   { xxx=>ASC , yyy : DESC ..... }
57303         
57304         var mstate = {};
57305         if (!this.ds.multiSort) { 
57306             var state = this.ds.getSortState();
57307             if(!state){
57308                 return;
57309             }
57310             mstate[state.field] = state.direction;
57311             // FIXME... - this is not used here.. but might be elsewhere..
57312             this.sortState = state;
57313             
57314         } else {
57315             mstate = this.ds.sortToggle;
57316         }
57317         //remove existing sort classes..
57318         
57319         var sc = this.sortClasses;
57320         var hds = this.el.select(this.headerSelector).removeClass(sc);
57321         
57322         for(var f in mstate) {
57323         
57324             var sortColumn = this.cm.findColumnIndex(f);
57325             
57326             if(sortColumn != -1){
57327                 var sortDir = mstate[f];        
57328                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57329             }
57330         }
57331         
57332          
57333         
57334     },
57335
57336
57337     handleHeaderClick : function(g, index,e){
57338         
57339         Roo.log("header click");
57340         
57341         if (Roo.isTouch) {
57342             // touch events on header are handled by context
57343             this.handleHdCtx(g,index,e);
57344             return;
57345         }
57346         
57347         
57348         if(this.headersDisabled){
57349             return;
57350         }
57351         var dm = g.dataSource, cm = g.colModel;
57352         if(!cm.isSortable(index)){
57353             return;
57354         }
57355         g.stopEditing();
57356         
57357         if (dm.multiSort) {
57358             // update the sortOrder
57359             var so = [];
57360             for(var i = 0; i < cm.config.length; i++ ) {
57361                 
57362                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57363                     continue; // dont' bother, it's not in sort list or being set.
57364                 }
57365                 
57366                 so.push(cm.config[i].dataIndex);
57367             };
57368             dm.sortOrder = so;
57369         }
57370         
57371         
57372         dm.sort(cm.getDataIndex(index));
57373     },
57374
57375
57376     destroy : function(){
57377         if(this.colMenu){
57378             this.colMenu.removeAll();
57379             Roo.menu.MenuMgr.unregister(this.colMenu);
57380             this.colMenu.getEl().remove();
57381             delete this.colMenu;
57382         }
57383         if(this.hmenu){
57384             this.hmenu.removeAll();
57385             Roo.menu.MenuMgr.unregister(this.hmenu);
57386             this.hmenu.getEl().remove();
57387             delete this.hmenu;
57388         }
57389         if(this.grid.enableColumnMove){
57390             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57391             if(dds){
57392                 for(var dd in dds){
57393                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57394                         var elid = dds[dd].dragElId;
57395                         dds[dd].unreg();
57396                         Roo.get(elid).remove();
57397                     } else if(dds[dd].config.isTarget){
57398                         dds[dd].proxyTop.remove();
57399                         dds[dd].proxyBottom.remove();
57400                         dds[dd].unreg();
57401                     }
57402                     if(Roo.dd.DDM.locationCache[dd]){
57403                         delete Roo.dd.DDM.locationCache[dd];
57404                     }
57405                 }
57406                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57407             }
57408         }
57409         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57410         this.bind(null, null);
57411         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57412     },
57413
57414     handleLockChange : function(){
57415         this.refresh(true);
57416     },
57417
57418     onDenyColumnLock : function(){
57419
57420     },
57421
57422     onDenyColumnHide : function(){
57423
57424     },
57425
57426     handleHdMenuClick : function(item){
57427         var index = this.hdCtxIndex;
57428         var cm = this.cm, ds = this.ds;
57429         switch(item.id){
57430             case "asc":
57431                 ds.sort(cm.getDataIndex(index), "ASC");
57432                 break;
57433             case "desc":
57434                 ds.sort(cm.getDataIndex(index), "DESC");
57435                 break;
57436             case "lock":
57437                 var lc = cm.getLockedCount();
57438                 if(cm.getColumnCount(true) <= lc+1){
57439                     this.onDenyColumnLock();
57440                     return;
57441                 }
57442                 if(lc != index){
57443                     cm.setLocked(index, true, true);
57444                     cm.moveColumn(index, lc);
57445                     this.grid.fireEvent("columnmove", index, lc);
57446                 }else{
57447                     cm.setLocked(index, true);
57448                 }
57449             break;
57450             case "unlock":
57451                 var lc = cm.getLockedCount();
57452                 if((lc-1) != index){
57453                     cm.setLocked(index, false, true);
57454                     cm.moveColumn(index, lc-1);
57455                     this.grid.fireEvent("columnmove", index, lc-1);
57456                 }else{
57457                     cm.setLocked(index, false);
57458                 }
57459             break;
57460             case 'wider': // used to expand cols on touch..
57461             case 'narrow':
57462                 var cw = cm.getColumnWidth(index);
57463                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57464                 cw = Math.max(0, cw);
57465                 cw = Math.min(cw,4000);
57466                 cm.setColumnWidth(index, cw);
57467                 break;
57468                 
57469             default:
57470                 index = cm.getIndexById(item.id.substr(4));
57471                 if(index != -1){
57472                     if(item.checked && cm.getColumnCount(true) <= 1){
57473                         this.onDenyColumnHide();
57474                         return false;
57475                     }
57476                     cm.setHidden(index, item.checked);
57477                 }
57478         }
57479         return true;
57480     },
57481
57482     beforeColMenuShow : function(){
57483         var cm = this.cm,  colCount = cm.getColumnCount();
57484         this.colMenu.removeAll();
57485         for(var i = 0; i < colCount; i++){
57486             this.colMenu.add(new Roo.menu.CheckItem({
57487                 id: "col-"+cm.getColumnId(i),
57488                 text: cm.getColumnHeader(i),
57489                 checked: !cm.isHidden(i),
57490                 hideOnClick:false
57491             }));
57492         }
57493     },
57494
57495     handleHdCtx : function(g, index, e){
57496         e.stopEvent();
57497         var hd = this.getHeaderCell(index);
57498         this.hdCtxIndex = index;
57499         var ms = this.hmenu.items, cm = this.cm;
57500         ms.get("asc").setDisabled(!cm.isSortable(index));
57501         ms.get("desc").setDisabled(!cm.isSortable(index));
57502         if(this.grid.enableColLock !== false){
57503             ms.get("lock").setDisabled(cm.isLocked(index));
57504             ms.get("unlock").setDisabled(!cm.isLocked(index));
57505         }
57506         this.hmenu.show(hd, "tl-bl");
57507     },
57508
57509     handleHdOver : function(e){
57510         var hd = this.findHeaderCell(e.getTarget());
57511         if(hd && !this.headersDisabled){
57512             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57513                this.fly(hd).addClass("x-grid-hd-over");
57514             }
57515         }
57516     },
57517
57518     handleHdOut : function(e){
57519         var hd = this.findHeaderCell(e.getTarget());
57520         if(hd){
57521             this.fly(hd).removeClass("x-grid-hd-over");
57522         }
57523     },
57524
57525     handleSplitDblClick : function(e, t){
57526         var i = this.getCellIndex(t);
57527         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57528             this.autoSizeColumn(i, true);
57529             this.layout();
57530         }
57531     },
57532
57533     render : function(){
57534
57535         var cm = this.cm;
57536         var colCount = cm.getColumnCount();
57537
57538         if(this.grid.monitorWindowResize === true){
57539             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57540         }
57541         var header = this.renderHeaders();
57542         var body = this.templates.body.apply({rows:""});
57543         var html = this.templates.master.apply({
57544             lockedBody: body,
57545             body: body,
57546             lockedHeader: header[0],
57547             header: header[1]
57548         });
57549
57550         //this.updateColumns();
57551
57552         this.grid.getGridEl().dom.innerHTML = html;
57553
57554         this.initElements();
57555         
57556         // a kludge to fix the random scolling effect in webkit
57557         this.el.on("scroll", function() {
57558             this.el.dom.scrollTop=0; // hopefully not recursive..
57559         },this);
57560
57561         this.scroller.on("scroll", this.handleScroll, this);
57562         this.lockedBody.on("mousewheel", this.handleWheel, this);
57563         this.mainBody.on("mousewheel", this.handleWheel, this);
57564
57565         this.mainHd.on("mouseover", this.handleHdOver, this);
57566         this.mainHd.on("mouseout", this.handleHdOut, this);
57567         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57568                 {delegate: "."+this.splitClass});
57569
57570         this.lockedHd.on("mouseover", this.handleHdOver, this);
57571         this.lockedHd.on("mouseout", this.handleHdOut, this);
57572         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57573                 {delegate: "."+this.splitClass});
57574
57575         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57576             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57577         }
57578
57579         this.updateSplitters();
57580
57581         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57582             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57583             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57584         }
57585
57586         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57587             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57588             this.hmenu.add(
57589                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57590                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57591             );
57592             if(this.grid.enableColLock !== false){
57593                 this.hmenu.add('-',
57594                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57595                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57596                 );
57597             }
57598             if (Roo.isTouch) {
57599                  this.hmenu.add('-',
57600                     {id:"wider", text: this.columnsWiderText},
57601                     {id:"narrow", text: this.columnsNarrowText }
57602                 );
57603                 
57604                  
57605             }
57606             
57607             if(this.grid.enableColumnHide !== false){
57608
57609                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57610                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57611                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57612
57613                 this.hmenu.add('-',
57614                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57615                 );
57616             }
57617             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57618
57619             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57620         }
57621
57622         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57623             this.dd = new Roo.grid.GridDragZone(this.grid, {
57624                 ddGroup : this.grid.ddGroup || 'GridDD'
57625             });
57626             
57627         }
57628
57629         /*
57630         for(var i = 0; i < colCount; i++){
57631             if(cm.isHidden(i)){
57632                 this.hideColumn(i);
57633             }
57634             if(cm.config[i].align){
57635                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57636                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57637             }
57638         }*/
57639         
57640         this.updateHeaderSortState();
57641
57642         this.beforeInitialResize();
57643         this.layout(true);
57644
57645         // two part rendering gives faster view to the user
57646         this.renderPhase2.defer(1, this);
57647     },
57648
57649     renderPhase2 : function(){
57650         // render the rows now
57651         this.refresh();
57652         if(this.grid.autoSizeColumns){
57653             this.autoSizeColumns();
57654         }
57655     },
57656
57657     beforeInitialResize : function(){
57658
57659     },
57660
57661     onColumnSplitterMoved : function(i, w){
57662         this.userResized = true;
57663         var cm = this.grid.colModel;
57664         cm.setColumnWidth(i, w, true);
57665         var cid = cm.getColumnId(i);
57666         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57667         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57668         this.updateSplitters();
57669         this.layout();
57670         this.grid.fireEvent("columnresize", i, w);
57671     },
57672
57673     syncRowHeights : function(startIndex, endIndex){
57674         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57675             startIndex = startIndex || 0;
57676             var mrows = this.getBodyTable().rows;
57677             var lrows = this.getLockedTable().rows;
57678             var len = mrows.length-1;
57679             endIndex = Math.min(endIndex || len, len);
57680             for(var i = startIndex; i <= endIndex; i++){
57681                 var m = mrows[i], l = lrows[i];
57682                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57683                 m.style.height = l.style.height = h + "px";
57684             }
57685         }
57686     },
57687
57688     layout : function(initialRender, is2ndPass){
57689         var g = this.grid;
57690         var auto = g.autoHeight;
57691         var scrollOffset = 16;
57692         var c = g.getGridEl(), cm = this.cm,
57693                 expandCol = g.autoExpandColumn,
57694                 gv = this;
57695         //c.beginMeasure();
57696
57697         if(!c.dom.offsetWidth){ // display:none?
57698             if(initialRender){
57699                 this.lockedWrap.show();
57700                 this.mainWrap.show();
57701             }
57702             return;
57703         }
57704
57705         var hasLock = this.cm.isLocked(0);
57706
57707         var tbh = this.headerPanel.getHeight();
57708         var bbh = this.footerPanel.getHeight();
57709
57710         if(auto){
57711             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57712             var newHeight = ch + c.getBorderWidth("tb");
57713             if(g.maxHeight){
57714                 newHeight = Math.min(g.maxHeight, newHeight);
57715             }
57716             c.setHeight(newHeight);
57717         }
57718
57719         if(g.autoWidth){
57720             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57721         }
57722
57723         var s = this.scroller;
57724
57725         var csize = c.getSize(true);
57726
57727         this.el.setSize(csize.width, csize.height);
57728
57729         this.headerPanel.setWidth(csize.width);
57730         this.footerPanel.setWidth(csize.width);
57731
57732         var hdHeight = this.mainHd.getHeight();
57733         var vw = csize.width;
57734         var vh = csize.height - (tbh + bbh);
57735
57736         s.setSize(vw, vh);
57737
57738         var bt = this.getBodyTable();
57739         
57740         if(cm.getLockedCount() == cm.config.length){
57741             bt = this.getLockedTable();
57742         }
57743         
57744         var ltWidth = hasLock ?
57745                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57746
57747         var scrollHeight = bt.offsetHeight;
57748         var scrollWidth = ltWidth + bt.offsetWidth;
57749         var vscroll = false, hscroll = false;
57750
57751         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57752
57753         var lw = this.lockedWrap, mw = this.mainWrap;
57754         var lb = this.lockedBody, mb = this.mainBody;
57755
57756         setTimeout(function(){
57757             var t = s.dom.offsetTop;
57758             var w = s.dom.clientWidth,
57759                 h = s.dom.clientHeight;
57760
57761             lw.setTop(t);
57762             lw.setSize(ltWidth, h);
57763
57764             mw.setLeftTop(ltWidth, t);
57765             mw.setSize(w-ltWidth, h);
57766
57767             lb.setHeight(h-hdHeight);
57768             mb.setHeight(h-hdHeight);
57769
57770             if(is2ndPass !== true && !gv.userResized && expandCol){
57771                 // high speed resize without full column calculation
57772                 
57773                 var ci = cm.getIndexById(expandCol);
57774                 if (ci < 0) {
57775                     ci = cm.findColumnIndex(expandCol);
57776                 }
57777                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57778                 var expandId = cm.getColumnId(ci);
57779                 var  tw = cm.getTotalWidth(false);
57780                 var currentWidth = cm.getColumnWidth(ci);
57781                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57782                 if(currentWidth != cw){
57783                     cm.setColumnWidth(ci, cw, true);
57784                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57785                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57786                     gv.updateSplitters();
57787                     gv.layout(false, true);
57788                 }
57789             }
57790
57791             if(initialRender){
57792                 lw.show();
57793                 mw.show();
57794             }
57795             //c.endMeasure();
57796         }, 10);
57797     },
57798
57799     onWindowResize : function(){
57800         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57801             return;
57802         }
57803         this.layout();
57804     },
57805
57806     appendFooter : function(parentEl){
57807         return null;
57808     },
57809
57810     sortAscText : "Sort Ascending",
57811     sortDescText : "Sort Descending",
57812     lockText : "Lock Column",
57813     unlockText : "Unlock Column",
57814     columnsText : "Columns",
57815  
57816     columnsWiderText : "Wider",
57817     columnsNarrowText : "Thinner"
57818 });
57819
57820
57821 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57822     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57823     this.proxy.el.addClass('x-grid3-col-dd');
57824 };
57825
57826 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57827     handleMouseDown : function(e){
57828
57829     },
57830
57831     callHandleMouseDown : function(e){
57832         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57833     }
57834 });
57835 /*
57836  * Based on:
57837  * Ext JS Library 1.1.1
57838  * Copyright(c) 2006-2007, Ext JS, LLC.
57839  *
57840  * Originally Released Under LGPL - original licence link has changed is not relivant.
57841  *
57842  * Fork - LGPL
57843  * <script type="text/javascript">
57844  */
57845  
57846 // private
57847 // This is a support class used internally by the Grid components
57848 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57849     this.grid = grid;
57850     this.view = grid.getView();
57851     this.proxy = this.view.resizeProxy;
57852     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57853         "gridSplitters" + this.grid.getGridEl().id, {
57854         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57855     });
57856     this.setHandleElId(Roo.id(hd));
57857     this.setOuterHandleElId(Roo.id(hd2));
57858     this.scroll = false;
57859 };
57860 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57861     fly: Roo.Element.fly,
57862
57863     b4StartDrag : function(x, y){
57864         this.view.headersDisabled = true;
57865         this.proxy.setHeight(this.view.mainWrap.getHeight());
57866         var w = this.cm.getColumnWidth(this.cellIndex);
57867         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57868         this.resetConstraints();
57869         this.setXConstraint(minw, 1000);
57870         this.setYConstraint(0, 0);
57871         this.minX = x - minw;
57872         this.maxX = x + 1000;
57873         this.startPos = x;
57874         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57875     },
57876
57877
57878     handleMouseDown : function(e){
57879         ev = Roo.EventObject.setEvent(e);
57880         var t = this.fly(ev.getTarget());
57881         if(t.hasClass("x-grid-split")){
57882             this.cellIndex = this.view.getCellIndex(t.dom);
57883             this.split = t.dom;
57884             this.cm = this.grid.colModel;
57885             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57886                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57887             }
57888         }
57889     },
57890
57891     endDrag : function(e){
57892         this.view.headersDisabled = false;
57893         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57894         var diff = endX - this.startPos;
57895         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57896     },
57897
57898     autoOffset : function(){
57899         this.setDelta(0,0);
57900     }
57901 });/*
57902  * Based on:
57903  * Ext JS Library 1.1.1
57904  * Copyright(c) 2006-2007, Ext JS, LLC.
57905  *
57906  * Originally Released Under LGPL - original licence link has changed is not relivant.
57907  *
57908  * Fork - LGPL
57909  * <script type="text/javascript">
57910  */
57911  
57912 // private
57913 // This is a support class used internally by the Grid components
57914 Roo.grid.GridDragZone = function(grid, config){
57915     this.view = grid.getView();
57916     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57917     if(this.view.lockedBody){
57918         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57919         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57920     }
57921     this.scroll = false;
57922     this.grid = grid;
57923     this.ddel = document.createElement('div');
57924     this.ddel.className = 'x-grid-dd-wrap';
57925 };
57926
57927 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57928     ddGroup : "GridDD",
57929
57930     getDragData : function(e){
57931         var t = Roo.lib.Event.getTarget(e);
57932         var rowIndex = this.view.findRowIndex(t);
57933         var sm = this.grid.selModel;
57934             
57935         //Roo.log(rowIndex);
57936         
57937         if (sm.getSelectedCell) {
57938             // cell selection..
57939             if (!sm.getSelectedCell()) {
57940                 return false;
57941             }
57942             if (rowIndex != sm.getSelectedCell()[0]) {
57943                 return false;
57944             }
57945         
57946         }
57947         
57948         if(rowIndex !== false){
57949             
57950             // if editorgrid.. 
57951             
57952             
57953             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57954                
57955             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57956               //  
57957             //}
57958             if (e.hasModifier()){
57959                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57960             }
57961             
57962             Roo.log("getDragData");
57963             
57964             return {
57965                 grid: this.grid,
57966                 ddel: this.ddel,
57967                 rowIndex: rowIndex,
57968                 selections:sm.getSelections ? sm.getSelections() : (
57969                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57970                 )
57971             };
57972         }
57973         return false;
57974     },
57975
57976     onInitDrag : function(e){
57977         var data = this.dragData;
57978         this.ddel.innerHTML = this.grid.getDragDropText();
57979         this.proxy.update(this.ddel);
57980         // fire start drag?
57981     },
57982
57983     afterRepair : function(){
57984         this.dragging = false;
57985     },
57986
57987     getRepairXY : function(e, data){
57988         return false;
57989     },
57990
57991     onEndDrag : function(data, e){
57992         // fire end drag?
57993     },
57994
57995     onValidDrop : function(dd, e, id){
57996         // fire drag drop?
57997         this.hideProxy();
57998     },
57999
58000     beforeInvalidDrop : function(e, id){
58001
58002     }
58003 });/*
58004  * Based on:
58005  * Ext JS Library 1.1.1
58006  * Copyright(c) 2006-2007, Ext JS, LLC.
58007  *
58008  * Originally Released Under LGPL - original licence link has changed is not relivant.
58009  *
58010  * Fork - LGPL
58011  * <script type="text/javascript">
58012  */
58013  
58014
58015 /**
58016  * @class Roo.grid.ColumnModel
58017  * @extends Roo.util.Observable
58018  * This is the default implementation of a ColumnModel used by the Grid. It defines
58019  * the columns in the grid.
58020  * <br>Usage:<br>
58021  <pre><code>
58022  var colModel = new Roo.grid.ColumnModel([
58023         {header: "Ticker", width: 60, sortable: true, locked: true},
58024         {header: "Company Name", width: 150, sortable: true},
58025         {header: "Market Cap.", width: 100, sortable: true},
58026         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58027         {header: "Employees", width: 100, sortable: true, resizable: false}
58028  ]);
58029  </code></pre>
58030  * <p>
58031  
58032  * The config options listed for this class are options which may appear in each
58033  * individual column definition.
58034  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58035  * @constructor
58036  * @param {Object} config An Array of column config objects. See this class's
58037  * config objects for details.
58038 */
58039 Roo.grid.ColumnModel = function(config){
58040         /**
58041      * The config passed into the constructor
58042      */
58043     this.config = config;
58044     this.lookup = {};
58045
58046     // if no id, create one
58047     // if the column does not have a dataIndex mapping,
58048     // map it to the order it is in the config
58049     for(var i = 0, len = config.length; i < len; i++){
58050         var c = config[i];
58051         if(typeof c.dataIndex == "undefined"){
58052             c.dataIndex = i;
58053         }
58054         if(typeof c.renderer == "string"){
58055             c.renderer = Roo.util.Format[c.renderer];
58056         }
58057         if(typeof c.id == "undefined"){
58058             c.id = Roo.id();
58059         }
58060         if(c.editor && c.editor.xtype){
58061             c.editor  = Roo.factory(c.editor, Roo.grid);
58062         }
58063         if(c.editor && c.editor.isFormField){
58064             c.editor = new Roo.grid.GridEditor(c.editor);
58065         }
58066         this.lookup[c.id] = c;
58067     }
58068
58069     /**
58070      * The width of columns which have no width specified (defaults to 100)
58071      * @type Number
58072      */
58073     this.defaultWidth = 100;
58074
58075     /**
58076      * Default sortable of columns which have no sortable specified (defaults to false)
58077      * @type Boolean
58078      */
58079     this.defaultSortable = false;
58080
58081     this.addEvents({
58082         /**
58083              * @event widthchange
58084              * Fires when the width of a column changes.
58085              * @param {ColumnModel} this
58086              * @param {Number} columnIndex The column index
58087              * @param {Number} newWidth The new width
58088              */
58089             "widthchange": true,
58090         /**
58091              * @event headerchange
58092              * Fires when the text of a header changes.
58093              * @param {ColumnModel} this
58094              * @param {Number} columnIndex The column index
58095              * @param {Number} newText The new header text
58096              */
58097             "headerchange": true,
58098         /**
58099              * @event hiddenchange
58100              * Fires when a column is hidden or "unhidden".
58101              * @param {ColumnModel} this
58102              * @param {Number} columnIndex The column index
58103              * @param {Boolean} hidden true if hidden, false otherwise
58104              */
58105             "hiddenchange": true,
58106             /**
58107          * @event columnmoved
58108          * Fires when a column is moved.
58109          * @param {ColumnModel} this
58110          * @param {Number} oldIndex
58111          * @param {Number} newIndex
58112          */
58113         "columnmoved" : true,
58114         /**
58115          * @event columlockchange
58116          * Fires when a column's locked state is changed
58117          * @param {ColumnModel} this
58118          * @param {Number} colIndex
58119          * @param {Boolean} locked true if locked
58120          */
58121         "columnlockchange" : true
58122     });
58123     Roo.grid.ColumnModel.superclass.constructor.call(this);
58124 };
58125 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58126     /**
58127      * @cfg {String} header The header text to display in the Grid view.
58128      */
58129     /**
58130      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58131      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58132      * specified, the column's index is used as an index into the Record's data Array.
58133      */
58134     /**
58135      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58136      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58137      */
58138     /**
58139      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58140      * Defaults to the value of the {@link #defaultSortable} property.
58141      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58142      */
58143     /**
58144      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58145      */
58146     /**
58147      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58148      */
58149     /**
58150      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58151      */
58152     /**
58153      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58154      */
58155     /**
58156      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58157      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58158      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58159      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58160      */
58161        /**
58162      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58163      */
58164     /**
58165      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58166      */
58167     /**
58168      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58169      */
58170     /**
58171      * @cfg {String} cursor (Optional)
58172      */
58173     /**
58174      * @cfg {String} tooltip (Optional)
58175      */
58176     /**
58177      * @cfg {Number} xs (Optional)
58178      */
58179     /**
58180      * @cfg {Number} sm (Optional)
58181      */
58182     /**
58183      * @cfg {Number} md (Optional)
58184      */
58185     /**
58186      * @cfg {Number} lg (Optional)
58187      */
58188     /**
58189      * Returns the id of the column at the specified index.
58190      * @param {Number} index The column index
58191      * @return {String} the id
58192      */
58193     getColumnId : function(index){
58194         return this.config[index].id;
58195     },
58196
58197     /**
58198      * Returns the column for a specified id.
58199      * @param {String} id The column id
58200      * @return {Object} the column
58201      */
58202     getColumnById : function(id){
58203         return this.lookup[id];
58204     },
58205
58206     
58207     /**
58208      * Returns the column for a specified dataIndex.
58209      * @param {String} dataIndex The column dataIndex
58210      * @return {Object|Boolean} the column or false if not found
58211      */
58212     getColumnByDataIndex: function(dataIndex){
58213         var index = this.findColumnIndex(dataIndex);
58214         return index > -1 ? this.config[index] : false;
58215     },
58216     
58217     /**
58218      * Returns the index for a specified column id.
58219      * @param {String} id The column id
58220      * @return {Number} the index, or -1 if not found
58221      */
58222     getIndexById : function(id){
58223         for(var i = 0, len = this.config.length; i < len; i++){
58224             if(this.config[i].id == id){
58225                 return i;
58226             }
58227         }
58228         return -1;
58229     },
58230     
58231     /**
58232      * Returns the index for a specified column dataIndex.
58233      * @param {String} dataIndex The column dataIndex
58234      * @return {Number} the index, or -1 if not found
58235      */
58236     
58237     findColumnIndex : function(dataIndex){
58238         for(var i = 0, len = this.config.length; i < len; i++){
58239             if(this.config[i].dataIndex == dataIndex){
58240                 return i;
58241             }
58242         }
58243         return -1;
58244     },
58245     
58246     
58247     moveColumn : function(oldIndex, newIndex){
58248         var c = this.config[oldIndex];
58249         this.config.splice(oldIndex, 1);
58250         this.config.splice(newIndex, 0, c);
58251         this.dataMap = null;
58252         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58253     },
58254
58255     isLocked : function(colIndex){
58256         return this.config[colIndex].locked === true;
58257     },
58258
58259     setLocked : function(colIndex, value, suppressEvent){
58260         if(this.isLocked(colIndex) == value){
58261             return;
58262         }
58263         this.config[colIndex].locked = value;
58264         if(!suppressEvent){
58265             this.fireEvent("columnlockchange", this, colIndex, value);
58266         }
58267     },
58268
58269     getTotalLockedWidth : function(){
58270         var totalWidth = 0;
58271         for(var i = 0; i < this.config.length; i++){
58272             if(this.isLocked(i) && !this.isHidden(i)){
58273                 this.totalWidth += this.getColumnWidth(i);
58274             }
58275         }
58276         return totalWidth;
58277     },
58278
58279     getLockedCount : function(){
58280         for(var i = 0, len = this.config.length; i < len; i++){
58281             if(!this.isLocked(i)){
58282                 return i;
58283             }
58284         }
58285         
58286         return this.config.length;
58287     },
58288
58289     /**
58290      * Returns the number of columns.
58291      * @return {Number}
58292      */
58293     getColumnCount : function(visibleOnly){
58294         if(visibleOnly === true){
58295             var c = 0;
58296             for(var i = 0, len = this.config.length; i < len; i++){
58297                 if(!this.isHidden(i)){
58298                     c++;
58299                 }
58300             }
58301             return c;
58302         }
58303         return this.config.length;
58304     },
58305
58306     /**
58307      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58308      * @param {Function} fn
58309      * @param {Object} scope (optional)
58310      * @return {Array} result
58311      */
58312     getColumnsBy : function(fn, scope){
58313         var r = [];
58314         for(var i = 0, len = this.config.length; i < len; i++){
58315             var c = this.config[i];
58316             if(fn.call(scope||this, c, i) === true){
58317                 r[r.length] = c;
58318             }
58319         }
58320         return r;
58321     },
58322
58323     /**
58324      * Returns true if the specified column is sortable.
58325      * @param {Number} col The column index
58326      * @return {Boolean}
58327      */
58328     isSortable : function(col){
58329         if(typeof this.config[col].sortable == "undefined"){
58330             return this.defaultSortable;
58331         }
58332         return this.config[col].sortable;
58333     },
58334
58335     /**
58336      * Returns the rendering (formatting) function defined for the column.
58337      * @param {Number} col The column index.
58338      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58339      */
58340     getRenderer : function(col){
58341         if(!this.config[col].renderer){
58342             return Roo.grid.ColumnModel.defaultRenderer;
58343         }
58344         return this.config[col].renderer;
58345     },
58346
58347     /**
58348      * Sets the rendering (formatting) function for a column.
58349      * @param {Number} col The column index
58350      * @param {Function} fn The function to use to process the cell's raw data
58351      * to return HTML markup for the grid view. The render function is called with
58352      * the following parameters:<ul>
58353      * <li>Data value.</li>
58354      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58355      * <li>css A CSS style string to apply to the table cell.</li>
58356      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58357      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58358      * <li>Row index</li>
58359      * <li>Column index</li>
58360      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58361      */
58362     setRenderer : function(col, fn){
58363         this.config[col].renderer = fn;
58364     },
58365
58366     /**
58367      * Returns the width for the specified column.
58368      * @param {Number} col The column index
58369      * @return {Number}
58370      */
58371     getColumnWidth : function(col){
58372         return this.config[col].width * 1 || this.defaultWidth;
58373     },
58374
58375     /**
58376      * Sets the width for a column.
58377      * @param {Number} col The column index
58378      * @param {Number} width The new width
58379      */
58380     setColumnWidth : function(col, width, suppressEvent){
58381         this.config[col].width = width;
58382         this.totalWidth = null;
58383         if(!suppressEvent){
58384              this.fireEvent("widthchange", this, col, width);
58385         }
58386     },
58387
58388     /**
58389      * Returns the total width of all columns.
58390      * @param {Boolean} includeHidden True to include hidden column widths
58391      * @return {Number}
58392      */
58393     getTotalWidth : function(includeHidden){
58394         if(!this.totalWidth){
58395             this.totalWidth = 0;
58396             for(var i = 0, len = this.config.length; i < len; i++){
58397                 if(includeHidden || !this.isHidden(i)){
58398                     this.totalWidth += this.getColumnWidth(i);
58399                 }
58400             }
58401         }
58402         return this.totalWidth;
58403     },
58404
58405     /**
58406      * Returns the header for the specified column.
58407      * @param {Number} col The column index
58408      * @return {String}
58409      */
58410     getColumnHeader : function(col){
58411         return this.config[col].header;
58412     },
58413
58414     /**
58415      * Sets the header for a column.
58416      * @param {Number} col The column index
58417      * @param {String} header The new header
58418      */
58419     setColumnHeader : function(col, header){
58420         this.config[col].header = header;
58421         this.fireEvent("headerchange", this, col, header);
58422     },
58423
58424     /**
58425      * Returns the tooltip for the specified column.
58426      * @param {Number} col The column index
58427      * @return {String}
58428      */
58429     getColumnTooltip : function(col){
58430             return this.config[col].tooltip;
58431     },
58432     /**
58433      * Sets the tooltip for a column.
58434      * @param {Number} col The column index
58435      * @param {String} tooltip The new tooltip
58436      */
58437     setColumnTooltip : function(col, tooltip){
58438             this.config[col].tooltip = tooltip;
58439     },
58440
58441     /**
58442      * Returns the dataIndex for the specified column.
58443      * @param {Number} col The column index
58444      * @return {Number}
58445      */
58446     getDataIndex : function(col){
58447         return this.config[col].dataIndex;
58448     },
58449
58450     /**
58451      * Sets the dataIndex for a column.
58452      * @param {Number} col The column index
58453      * @param {Number} dataIndex The new dataIndex
58454      */
58455     setDataIndex : function(col, dataIndex){
58456         this.config[col].dataIndex = dataIndex;
58457     },
58458
58459     
58460     
58461     /**
58462      * Returns true if the cell is editable.
58463      * @param {Number} colIndex The column index
58464      * @param {Number} rowIndex The row index - this is nto actually used..?
58465      * @return {Boolean}
58466      */
58467     isCellEditable : function(colIndex, rowIndex){
58468         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58469     },
58470
58471     /**
58472      * Returns the editor defined for the cell/column.
58473      * return false or null to disable editing.
58474      * @param {Number} colIndex The column index
58475      * @param {Number} rowIndex The row index
58476      * @return {Object}
58477      */
58478     getCellEditor : function(colIndex, rowIndex){
58479         return this.config[colIndex].editor;
58480     },
58481
58482     /**
58483      * Sets if a column is editable.
58484      * @param {Number} col The column index
58485      * @param {Boolean} editable True if the column is editable
58486      */
58487     setEditable : function(col, editable){
58488         this.config[col].editable = editable;
58489     },
58490
58491
58492     /**
58493      * Returns true if the column is hidden.
58494      * @param {Number} colIndex The column index
58495      * @return {Boolean}
58496      */
58497     isHidden : function(colIndex){
58498         return this.config[colIndex].hidden;
58499     },
58500
58501
58502     /**
58503      * Returns true if the column width cannot be changed
58504      */
58505     isFixed : function(colIndex){
58506         return this.config[colIndex].fixed;
58507     },
58508
58509     /**
58510      * Returns true if the column can be resized
58511      * @return {Boolean}
58512      */
58513     isResizable : function(colIndex){
58514         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58515     },
58516     /**
58517      * Sets if a column is hidden.
58518      * @param {Number} colIndex The column index
58519      * @param {Boolean} hidden True if the column is hidden
58520      */
58521     setHidden : function(colIndex, hidden){
58522         this.config[colIndex].hidden = hidden;
58523         this.totalWidth = null;
58524         this.fireEvent("hiddenchange", this, colIndex, hidden);
58525     },
58526
58527     /**
58528      * Sets the editor for a column.
58529      * @param {Number} col The column index
58530      * @param {Object} editor The editor object
58531      */
58532     setEditor : function(col, editor){
58533         this.config[col].editor = editor;
58534     }
58535 });
58536
58537 Roo.grid.ColumnModel.defaultRenderer = function(value)
58538 {
58539     if(typeof value == "object") {
58540         return value;
58541     }
58542         if(typeof value == "string" && value.length < 1){
58543             return "&#160;";
58544         }
58545     
58546         return String.format("{0}", value);
58547 };
58548
58549 // Alias for backwards compatibility
58550 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58551 /*
58552  * Based on:
58553  * Ext JS Library 1.1.1
58554  * Copyright(c) 2006-2007, Ext JS, LLC.
58555  *
58556  * Originally Released Under LGPL - original licence link has changed is not relivant.
58557  *
58558  * Fork - LGPL
58559  * <script type="text/javascript">
58560  */
58561
58562 /**
58563  * @class Roo.grid.AbstractSelectionModel
58564  * @extends Roo.util.Observable
58565  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58566  * implemented by descendant classes.  This class should not be directly instantiated.
58567  * @constructor
58568  */
58569 Roo.grid.AbstractSelectionModel = function(){
58570     this.locked = false;
58571     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58572 };
58573
58574 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58575     /** @ignore Called by the grid automatically. Do not call directly. */
58576     init : function(grid){
58577         this.grid = grid;
58578         this.initEvents();
58579     },
58580
58581     /**
58582      * Locks the selections.
58583      */
58584     lock : function(){
58585         this.locked = true;
58586     },
58587
58588     /**
58589      * Unlocks the selections.
58590      */
58591     unlock : function(){
58592         this.locked = false;
58593     },
58594
58595     /**
58596      * Returns true if the selections are locked.
58597      * @return {Boolean}
58598      */
58599     isLocked : function(){
58600         return this.locked;
58601     }
58602 });/*
58603  * Based on:
58604  * Ext JS Library 1.1.1
58605  * Copyright(c) 2006-2007, Ext JS, LLC.
58606  *
58607  * Originally Released Under LGPL - original licence link has changed is not relivant.
58608  *
58609  * Fork - LGPL
58610  * <script type="text/javascript">
58611  */
58612 /**
58613  * @extends Roo.grid.AbstractSelectionModel
58614  * @class Roo.grid.RowSelectionModel
58615  * The default SelectionModel used by {@link Roo.grid.Grid}.
58616  * It supports multiple selections and keyboard selection/navigation. 
58617  * @constructor
58618  * @param {Object} config
58619  */
58620 Roo.grid.RowSelectionModel = function(config){
58621     Roo.apply(this, config);
58622     this.selections = new Roo.util.MixedCollection(false, function(o){
58623         return o.id;
58624     });
58625
58626     this.last = false;
58627     this.lastActive = false;
58628
58629     this.addEvents({
58630         /**
58631              * @event selectionchange
58632              * Fires when the selection changes
58633              * @param {SelectionModel} this
58634              */
58635             "selectionchange" : true,
58636         /**
58637              * @event afterselectionchange
58638              * Fires after the selection changes (eg. by key press or clicking)
58639              * @param {SelectionModel} this
58640              */
58641             "afterselectionchange" : true,
58642         /**
58643              * @event beforerowselect
58644              * Fires when a row is selected being selected, return false to cancel.
58645              * @param {SelectionModel} this
58646              * @param {Number} rowIndex The selected index
58647              * @param {Boolean} keepExisting False if other selections will be cleared
58648              */
58649             "beforerowselect" : true,
58650         /**
58651              * @event rowselect
58652              * Fires when a row is selected.
58653              * @param {SelectionModel} this
58654              * @param {Number} rowIndex The selected index
58655              * @param {Roo.data.Record} r The record
58656              */
58657             "rowselect" : true,
58658         /**
58659              * @event rowdeselect
58660              * Fires when a row is deselected.
58661              * @param {SelectionModel} this
58662              * @param {Number} rowIndex The selected index
58663              */
58664         "rowdeselect" : true
58665     });
58666     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58667     this.locked = false;
58668 };
58669
58670 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58671     /**
58672      * @cfg {Boolean} singleSelect
58673      * True to allow selection of only one row at a time (defaults to false)
58674      */
58675     singleSelect : false,
58676
58677     // private
58678     initEvents : function(){
58679
58680         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58681             this.grid.on("mousedown", this.handleMouseDown, this);
58682         }else{ // allow click to work like normal
58683             this.grid.on("rowclick", this.handleDragableRowClick, this);
58684         }
58685
58686         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58687             "up" : function(e){
58688                 if(!e.shiftKey){
58689                     this.selectPrevious(e.shiftKey);
58690                 }else if(this.last !== false && this.lastActive !== false){
58691                     var last = this.last;
58692                     this.selectRange(this.last,  this.lastActive-1);
58693                     this.grid.getView().focusRow(this.lastActive);
58694                     if(last !== false){
58695                         this.last = last;
58696                     }
58697                 }else{
58698                     this.selectFirstRow();
58699                 }
58700                 this.fireEvent("afterselectionchange", this);
58701             },
58702             "down" : function(e){
58703                 if(!e.shiftKey){
58704                     this.selectNext(e.shiftKey);
58705                 }else if(this.last !== false && this.lastActive !== false){
58706                     var last = this.last;
58707                     this.selectRange(this.last,  this.lastActive+1);
58708                     this.grid.getView().focusRow(this.lastActive);
58709                     if(last !== false){
58710                         this.last = last;
58711                     }
58712                 }else{
58713                     this.selectFirstRow();
58714                 }
58715                 this.fireEvent("afterselectionchange", this);
58716             },
58717             scope: this
58718         });
58719
58720         var view = this.grid.view;
58721         view.on("refresh", this.onRefresh, this);
58722         view.on("rowupdated", this.onRowUpdated, this);
58723         view.on("rowremoved", this.onRemove, this);
58724     },
58725
58726     // private
58727     onRefresh : function(){
58728         var ds = this.grid.dataSource, i, v = this.grid.view;
58729         var s = this.selections;
58730         s.each(function(r){
58731             if((i = ds.indexOfId(r.id)) != -1){
58732                 v.onRowSelect(i);
58733                 s.add(ds.getAt(i)); // updating the selection relate data
58734             }else{
58735                 s.remove(r);
58736             }
58737         });
58738     },
58739
58740     // private
58741     onRemove : function(v, index, r){
58742         this.selections.remove(r);
58743     },
58744
58745     // private
58746     onRowUpdated : function(v, index, r){
58747         if(this.isSelected(r)){
58748             v.onRowSelect(index);
58749         }
58750     },
58751
58752     /**
58753      * Select records.
58754      * @param {Array} records The records to select
58755      * @param {Boolean} keepExisting (optional) True to keep existing selections
58756      */
58757     selectRecords : function(records, keepExisting){
58758         if(!keepExisting){
58759             this.clearSelections();
58760         }
58761         var ds = this.grid.dataSource;
58762         for(var i = 0, len = records.length; i < len; i++){
58763             this.selectRow(ds.indexOf(records[i]), true);
58764         }
58765     },
58766
58767     /**
58768      * Gets the number of selected rows.
58769      * @return {Number}
58770      */
58771     getCount : function(){
58772         return this.selections.length;
58773     },
58774
58775     /**
58776      * Selects the first row in the grid.
58777      */
58778     selectFirstRow : function(){
58779         this.selectRow(0);
58780     },
58781
58782     /**
58783      * Select the last row.
58784      * @param {Boolean} keepExisting (optional) True to keep existing selections
58785      */
58786     selectLastRow : function(keepExisting){
58787         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58788     },
58789
58790     /**
58791      * Selects the row immediately following the last selected row.
58792      * @param {Boolean} keepExisting (optional) True to keep existing selections
58793      */
58794     selectNext : function(keepExisting){
58795         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58796             this.selectRow(this.last+1, keepExisting);
58797             this.grid.getView().focusRow(this.last);
58798         }
58799     },
58800
58801     /**
58802      * Selects the row that precedes the last selected row.
58803      * @param {Boolean} keepExisting (optional) True to keep existing selections
58804      */
58805     selectPrevious : function(keepExisting){
58806         if(this.last){
58807             this.selectRow(this.last-1, keepExisting);
58808             this.grid.getView().focusRow(this.last);
58809         }
58810     },
58811
58812     /**
58813      * Returns the selected records
58814      * @return {Array} Array of selected records
58815      */
58816     getSelections : function(){
58817         return [].concat(this.selections.items);
58818     },
58819
58820     /**
58821      * Returns the first selected record.
58822      * @return {Record}
58823      */
58824     getSelected : function(){
58825         return this.selections.itemAt(0);
58826     },
58827
58828
58829     /**
58830      * Clears all selections.
58831      */
58832     clearSelections : function(fast){
58833         if(this.locked) {
58834             return;
58835         }
58836         if(fast !== true){
58837             var ds = this.grid.dataSource;
58838             var s = this.selections;
58839             s.each(function(r){
58840                 this.deselectRow(ds.indexOfId(r.id));
58841             }, this);
58842             s.clear();
58843         }else{
58844             this.selections.clear();
58845         }
58846         this.last = false;
58847     },
58848
58849
58850     /**
58851      * Selects all rows.
58852      */
58853     selectAll : function(){
58854         if(this.locked) {
58855             return;
58856         }
58857         this.selections.clear();
58858         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58859             this.selectRow(i, true);
58860         }
58861     },
58862
58863     /**
58864      * Returns True if there is a selection.
58865      * @return {Boolean}
58866      */
58867     hasSelection : function(){
58868         return this.selections.length > 0;
58869     },
58870
58871     /**
58872      * Returns True if the specified row is selected.
58873      * @param {Number/Record} record The record or index of the record to check
58874      * @return {Boolean}
58875      */
58876     isSelected : function(index){
58877         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58878         return (r && this.selections.key(r.id) ? true : false);
58879     },
58880
58881     /**
58882      * Returns True if the specified record id is selected.
58883      * @param {String} id The id of record to check
58884      * @return {Boolean}
58885      */
58886     isIdSelected : function(id){
58887         return (this.selections.key(id) ? true : false);
58888     },
58889
58890     // private
58891     handleMouseDown : function(e, t){
58892         var view = this.grid.getView(), rowIndex;
58893         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58894             return;
58895         };
58896         if(e.shiftKey && this.last !== false){
58897             var last = this.last;
58898             this.selectRange(last, rowIndex, e.ctrlKey);
58899             this.last = last; // reset the last
58900             view.focusRow(rowIndex);
58901         }else{
58902             var isSelected = this.isSelected(rowIndex);
58903             if(e.button !== 0 && isSelected){
58904                 view.focusRow(rowIndex);
58905             }else if(e.ctrlKey && isSelected){
58906                 this.deselectRow(rowIndex);
58907             }else if(!isSelected){
58908                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58909                 view.focusRow(rowIndex);
58910             }
58911         }
58912         this.fireEvent("afterselectionchange", this);
58913     },
58914     // private
58915     handleDragableRowClick :  function(grid, rowIndex, e) 
58916     {
58917         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58918             this.selectRow(rowIndex, false);
58919             grid.view.focusRow(rowIndex);
58920              this.fireEvent("afterselectionchange", this);
58921         }
58922     },
58923     
58924     /**
58925      * Selects multiple rows.
58926      * @param {Array} rows Array of the indexes of the row to select
58927      * @param {Boolean} keepExisting (optional) True to keep existing selections
58928      */
58929     selectRows : function(rows, keepExisting){
58930         if(!keepExisting){
58931             this.clearSelections();
58932         }
58933         for(var i = 0, len = rows.length; i < len; i++){
58934             this.selectRow(rows[i], true);
58935         }
58936     },
58937
58938     /**
58939      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58940      * @param {Number} startRow The index of the first row in the range
58941      * @param {Number} endRow The index of the last row in the range
58942      * @param {Boolean} keepExisting (optional) True to retain existing selections
58943      */
58944     selectRange : function(startRow, endRow, keepExisting){
58945         if(this.locked) {
58946             return;
58947         }
58948         if(!keepExisting){
58949             this.clearSelections();
58950         }
58951         if(startRow <= endRow){
58952             for(var i = startRow; i <= endRow; i++){
58953                 this.selectRow(i, true);
58954             }
58955         }else{
58956             for(var i = startRow; i >= endRow; i--){
58957                 this.selectRow(i, true);
58958             }
58959         }
58960     },
58961
58962     /**
58963      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58964      * @param {Number} startRow The index of the first row in the range
58965      * @param {Number} endRow The index of the last row in the range
58966      */
58967     deselectRange : function(startRow, endRow, preventViewNotify){
58968         if(this.locked) {
58969             return;
58970         }
58971         for(var i = startRow; i <= endRow; i++){
58972             this.deselectRow(i, preventViewNotify);
58973         }
58974     },
58975
58976     /**
58977      * Selects a row.
58978      * @param {Number} row The index of the row to select
58979      * @param {Boolean} keepExisting (optional) True to keep existing selections
58980      */
58981     selectRow : function(index, keepExisting, preventViewNotify){
58982         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58983             return;
58984         }
58985         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58986             if(!keepExisting || this.singleSelect){
58987                 this.clearSelections();
58988             }
58989             var r = this.grid.dataSource.getAt(index);
58990             this.selections.add(r);
58991             this.last = this.lastActive = index;
58992             if(!preventViewNotify){
58993                 this.grid.getView().onRowSelect(index);
58994             }
58995             this.fireEvent("rowselect", this, index, r);
58996             this.fireEvent("selectionchange", this);
58997         }
58998     },
58999
59000     /**
59001      * Deselects a row.
59002      * @param {Number} row The index of the row to deselect
59003      */
59004     deselectRow : function(index, preventViewNotify){
59005         if(this.locked) {
59006             return;
59007         }
59008         if(this.last == index){
59009             this.last = false;
59010         }
59011         if(this.lastActive == index){
59012             this.lastActive = false;
59013         }
59014         var r = this.grid.dataSource.getAt(index);
59015         this.selections.remove(r);
59016         if(!preventViewNotify){
59017             this.grid.getView().onRowDeselect(index);
59018         }
59019         this.fireEvent("rowdeselect", this, index);
59020         this.fireEvent("selectionchange", this);
59021     },
59022
59023     // private
59024     restoreLast : function(){
59025         if(this._last){
59026             this.last = this._last;
59027         }
59028     },
59029
59030     // private
59031     acceptsNav : function(row, col, cm){
59032         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59033     },
59034
59035     // private
59036     onEditorKey : function(field, e){
59037         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59038         if(k == e.TAB){
59039             e.stopEvent();
59040             ed.completeEdit();
59041             if(e.shiftKey){
59042                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59043             }else{
59044                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59045             }
59046         }else if(k == e.ENTER && !e.ctrlKey){
59047             e.stopEvent();
59048             ed.completeEdit();
59049             if(e.shiftKey){
59050                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59051             }else{
59052                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59053             }
59054         }else if(k == e.ESC){
59055             ed.cancelEdit();
59056         }
59057         if(newCell){
59058             g.startEditing(newCell[0], newCell[1]);
59059         }
59060     }
59061 });/*
59062  * Based on:
59063  * Ext JS Library 1.1.1
59064  * Copyright(c) 2006-2007, Ext JS, LLC.
59065  *
59066  * Originally Released Under LGPL - original licence link has changed is not relivant.
59067  *
59068  * Fork - LGPL
59069  * <script type="text/javascript">
59070  */
59071 /**
59072  * @class Roo.grid.CellSelectionModel
59073  * @extends Roo.grid.AbstractSelectionModel
59074  * This class provides the basic implementation for cell selection in a grid.
59075  * @constructor
59076  * @param {Object} config The object containing the configuration of this model.
59077  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59078  */
59079 Roo.grid.CellSelectionModel = function(config){
59080     Roo.apply(this, config);
59081
59082     this.selection = null;
59083
59084     this.addEvents({
59085         /**
59086              * @event beforerowselect
59087              * Fires before a cell is selected.
59088              * @param {SelectionModel} this
59089              * @param {Number} rowIndex The selected row index
59090              * @param {Number} colIndex The selected cell index
59091              */
59092             "beforecellselect" : true,
59093         /**
59094              * @event cellselect
59095              * Fires when a cell is selected.
59096              * @param {SelectionModel} this
59097              * @param {Number} rowIndex The selected row index
59098              * @param {Number} colIndex The selected cell index
59099              */
59100             "cellselect" : true,
59101         /**
59102              * @event selectionchange
59103              * Fires when the active selection changes.
59104              * @param {SelectionModel} this
59105              * @param {Object} selection null for no selection or an object (o) with two properties
59106                 <ul>
59107                 <li>o.record: the record object for the row the selection is in</li>
59108                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59109                 </ul>
59110              */
59111             "selectionchange" : true,
59112         /**
59113              * @event tabend
59114              * Fires when the tab (or enter) was pressed on the last editable cell
59115              * You can use this to trigger add new row.
59116              * @param {SelectionModel} this
59117              */
59118             "tabend" : true,
59119          /**
59120              * @event beforeeditnext
59121              * Fires before the next editable sell is made active
59122              * You can use this to skip to another cell or fire the tabend
59123              *    if you set cell to false
59124              * @param {Object} eventdata object : { cell : [ row, col ] } 
59125              */
59126             "beforeeditnext" : true
59127     });
59128     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59129 };
59130
59131 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59132     
59133     enter_is_tab: false,
59134
59135     /** @ignore */
59136     initEvents : function(){
59137         this.grid.on("mousedown", this.handleMouseDown, this);
59138         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59139         var view = this.grid.view;
59140         view.on("refresh", this.onViewChange, this);
59141         view.on("rowupdated", this.onRowUpdated, this);
59142         view.on("beforerowremoved", this.clearSelections, this);
59143         view.on("beforerowsinserted", this.clearSelections, this);
59144         if(this.grid.isEditor){
59145             this.grid.on("beforeedit", this.beforeEdit,  this);
59146         }
59147     },
59148
59149         //private
59150     beforeEdit : function(e){
59151         this.select(e.row, e.column, false, true, e.record);
59152     },
59153
59154         //private
59155     onRowUpdated : function(v, index, r){
59156         if(this.selection && this.selection.record == r){
59157             v.onCellSelect(index, this.selection.cell[1]);
59158         }
59159     },
59160
59161         //private
59162     onViewChange : function(){
59163         this.clearSelections(true);
59164     },
59165
59166         /**
59167          * Returns the currently selected cell,.
59168          * @return {Array} The selected cell (row, column) or null if none selected.
59169          */
59170     getSelectedCell : function(){
59171         return this.selection ? this.selection.cell : null;
59172     },
59173
59174     /**
59175      * Clears all selections.
59176      * @param {Boolean} true to prevent the gridview from being notified about the change.
59177      */
59178     clearSelections : function(preventNotify){
59179         var s = this.selection;
59180         if(s){
59181             if(preventNotify !== true){
59182                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59183             }
59184             this.selection = null;
59185             this.fireEvent("selectionchange", this, null);
59186         }
59187     },
59188
59189     /**
59190      * Returns true if there is a selection.
59191      * @return {Boolean}
59192      */
59193     hasSelection : function(){
59194         return this.selection ? true : false;
59195     },
59196
59197     /** @ignore */
59198     handleMouseDown : function(e, t){
59199         var v = this.grid.getView();
59200         if(this.isLocked()){
59201             return;
59202         };
59203         var row = v.findRowIndex(t);
59204         var cell = v.findCellIndex(t);
59205         if(row !== false && cell !== false){
59206             this.select(row, cell);
59207         }
59208     },
59209
59210     /**
59211      * Selects a cell.
59212      * @param {Number} rowIndex
59213      * @param {Number} collIndex
59214      */
59215     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59216         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59217             this.clearSelections();
59218             r = r || this.grid.dataSource.getAt(rowIndex);
59219             this.selection = {
59220                 record : r,
59221                 cell : [rowIndex, colIndex]
59222             };
59223             if(!preventViewNotify){
59224                 var v = this.grid.getView();
59225                 v.onCellSelect(rowIndex, colIndex);
59226                 if(preventFocus !== true){
59227                     v.focusCell(rowIndex, colIndex);
59228                 }
59229             }
59230             this.fireEvent("cellselect", this, rowIndex, colIndex);
59231             this.fireEvent("selectionchange", this, this.selection);
59232         }
59233     },
59234
59235         //private
59236     isSelectable : function(rowIndex, colIndex, cm){
59237         return !cm.isHidden(colIndex);
59238     },
59239
59240     /** @ignore */
59241     handleKeyDown : function(e){
59242         //Roo.log('Cell Sel Model handleKeyDown');
59243         if(!e.isNavKeyPress()){
59244             return;
59245         }
59246         var g = this.grid, s = this.selection;
59247         if(!s){
59248             e.stopEvent();
59249             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59250             if(cell){
59251                 this.select(cell[0], cell[1]);
59252             }
59253             return;
59254         }
59255         var sm = this;
59256         var walk = function(row, col, step){
59257             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59258         };
59259         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59260         var newCell;
59261
59262       
59263
59264         switch(k){
59265             case e.TAB:
59266                 // handled by onEditorKey
59267                 if (g.isEditor && g.editing) {
59268                     return;
59269                 }
59270                 if(e.shiftKey) {
59271                     newCell = walk(r, c-1, -1);
59272                 } else {
59273                     newCell = walk(r, c+1, 1);
59274                 }
59275                 break;
59276             
59277             case e.DOWN:
59278                newCell = walk(r+1, c, 1);
59279                 break;
59280             
59281             case e.UP:
59282                 newCell = walk(r-1, c, -1);
59283                 break;
59284             
59285             case e.RIGHT:
59286                 newCell = walk(r, c+1, 1);
59287                 break;
59288             
59289             case e.LEFT:
59290                 newCell = walk(r, c-1, -1);
59291                 break;
59292             
59293             case e.ENTER:
59294                 
59295                 if(g.isEditor && !g.editing){
59296                    g.startEditing(r, c);
59297                    e.stopEvent();
59298                    return;
59299                 }
59300                 
59301                 
59302              break;
59303         };
59304         if(newCell){
59305             this.select(newCell[0], newCell[1]);
59306             e.stopEvent();
59307             
59308         }
59309     },
59310
59311     acceptsNav : function(row, col, cm){
59312         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59313     },
59314     /**
59315      * Selects a cell.
59316      * @param {Number} field (not used) - as it's normally used as a listener
59317      * @param {Number} e - event - fake it by using
59318      *
59319      * var e = Roo.EventObjectImpl.prototype;
59320      * e.keyCode = e.TAB
59321      *
59322      * 
59323      */
59324     onEditorKey : function(field, e){
59325         
59326         var k = e.getKey(),
59327             newCell,
59328             g = this.grid,
59329             ed = g.activeEditor,
59330             forward = false;
59331         ///Roo.log('onEditorKey' + k);
59332         
59333         
59334         if (this.enter_is_tab && k == e.ENTER) {
59335             k = e.TAB;
59336         }
59337         
59338         if(k == e.TAB){
59339             if(e.shiftKey){
59340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59341             }else{
59342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59343                 forward = true;
59344             }
59345             
59346             e.stopEvent();
59347             
59348         } else if(k == e.ENTER &&  !e.ctrlKey){
59349             ed.completeEdit();
59350             e.stopEvent();
59351             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59352         
59353                 } else if(k == e.ESC){
59354             ed.cancelEdit();
59355         }
59356                 
59357         if (newCell) {
59358             var ecall = { cell : newCell, forward : forward };
59359             this.fireEvent('beforeeditnext', ecall );
59360             newCell = ecall.cell;
59361                         forward = ecall.forward;
59362         }
59363                 
59364         if(newCell){
59365             //Roo.log('next cell after edit');
59366             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59367         } else if (forward) {
59368             // tabbed past last
59369             this.fireEvent.defer(100, this, ['tabend',this]);
59370         }
59371     }
59372 });/*
59373  * Based on:
59374  * Ext JS Library 1.1.1
59375  * Copyright(c) 2006-2007, Ext JS, LLC.
59376  *
59377  * Originally Released Under LGPL - original licence link has changed is not relivant.
59378  *
59379  * Fork - LGPL
59380  * <script type="text/javascript">
59381  */
59382  
59383 /**
59384  * @class Roo.grid.EditorGrid
59385  * @extends Roo.grid.Grid
59386  * Class for creating and editable grid.
59387  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59388  * The container MUST have some type of size defined for the grid to fill. The container will be 
59389  * automatically set to position relative if it isn't already.
59390  * @param {Object} dataSource The data model to bind to
59391  * @param {Object} colModel The column model with info about this grid's columns
59392  */
59393 Roo.grid.EditorGrid = function(container, config){
59394     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59395     this.getGridEl().addClass("xedit-grid");
59396
59397     if(!this.selModel){
59398         this.selModel = new Roo.grid.CellSelectionModel();
59399     }
59400
59401     this.activeEditor = null;
59402
59403         this.addEvents({
59404             /**
59405              * @event beforeedit
59406              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59407              * <ul style="padding:5px;padding-left:16px;">
59408              * <li>grid - This grid</li>
59409              * <li>record - The record being edited</li>
59410              * <li>field - The field name being edited</li>
59411              * <li>value - The value for the field being edited.</li>
59412              * <li>row - The grid row index</li>
59413              * <li>column - The grid column index</li>
59414              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59415              * </ul>
59416              * @param {Object} e An edit event (see above for description)
59417              */
59418             "beforeedit" : true,
59419             /**
59420              * @event afteredit
59421              * Fires after a cell is edited. <br />
59422              * <ul style="padding:5px;padding-left:16px;">
59423              * <li>grid - This grid</li>
59424              * <li>record - The record being edited</li>
59425              * <li>field - The field name being edited</li>
59426              * <li>value - The value being set</li>
59427              * <li>originalValue - The original value for the field, before the edit.</li>
59428              * <li>row - The grid row index</li>
59429              * <li>column - The grid column index</li>
59430              * </ul>
59431              * @param {Object} e An edit event (see above for description)
59432              */
59433             "afteredit" : true,
59434             /**
59435              * @event validateedit
59436              * Fires after a cell is edited, but before the value is set in the record. 
59437          * You can use this to modify the value being set in the field, Return false
59438              * to cancel the change. The edit event object has the following properties <br />
59439              * <ul style="padding:5px;padding-left:16px;">
59440          * <li>editor - This editor</li>
59441              * <li>grid - This grid</li>
59442              * <li>record - The record being edited</li>
59443              * <li>field - The field name being edited</li>
59444              * <li>value - The value being set</li>
59445              * <li>originalValue - The original value for the field, before the edit.</li>
59446              * <li>row - The grid row index</li>
59447              * <li>column - The grid column index</li>
59448              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59449              * </ul>
59450              * @param {Object} e An edit event (see above for description)
59451              */
59452             "validateedit" : true
59453         });
59454     this.on("bodyscroll", this.stopEditing,  this);
59455     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59456 };
59457
59458 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59459     /**
59460      * @cfg {Number} clicksToEdit
59461      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59462      */
59463     clicksToEdit: 2,
59464
59465     // private
59466     isEditor : true,
59467     // private
59468     trackMouseOver: false, // causes very odd FF errors
59469
59470     onCellDblClick : function(g, row, col){
59471         this.startEditing(row, col);
59472     },
59473
59474     onEditComplete : function(ed, value, startValue){
59475         this.editing = false;
59476         this.activeEditor = null;
59477         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59478         var r = ed.record;
59479         var field = this.colModel.getDataIndex(ed.col);
59480         var e = {
59481             grid: this,
59482             record: r,
59483             field: field,
59484             originalValue: startValue,
59485             value: value,
59486             row: ed.row,
59487             column: ed.col,
59488             cancel:false,
59489             editor: ed
59490         };
59491         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59492         cell.show();
59493           
59494         if(String(value) !== String(startValue)){
59495             
59496             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59497                 r.set(field, e.value);
59498                 // if we are dealing with a combo box..
59499                 // then we also set the 'name' colum to be the displayField
59500                 if (ed.field.displayField && ed.field.name) {
59501                     r.set(ed.field.name, ed.field.el.dom.value);
59502                 }
59503                 
59504                 delete e.cancel; //?? why!!!
59505                 this.fireEvent("afteredit", e);
59506             }
59507         } else {
59508             this.fireEvent("afteredit", e); // always fire it!
59509         }
59510         this.view.focusCell(ed.row, ed.col);
59511     },
59512
59513     /**
59514      * Starts editing the specified for the specified row/column
59515      * @param {Number} rowIndex
59516      * @param {Number} colIndex
59517      */
59518     startEditing : function(row, col){
59519         this.stopEditing();
59520         if(this.colModel.isCellEditable(col, row)){
59521             this.view.ensureVisible(row, col, true);
59522           
59523             var r = this.dataSource.getAt(row);
59524             var field = this.colModel.getDataIndex(col);
59525             var cell = Roo.get(this.view.getCell(row,col));
59526             var e = {
59527                 grid: this,
59528                 record: r,
59529                 field: field,
59530                 value: r.data[field],
59531                 row: row,
59532                 column: col,
59533                 cancel:false 
59534             };
59535             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59536                 this.editing = true;
59537                 var ed = this.colModel.getCellEditor(col, row);
59538                 
59539                 if (!ed) {
59540                     return;
59541                 }
59542                 if(!ed.rendered){
59543                     ed.render(ed.parentEl || document.body);
59544                 }
59545                 ed.field.reset();
59546                
59547                 cell.hide();
59548                 
59549                 (function(){ // complex but required for focus issues in safari, ie and opera
59550                     ed.row = row;
59551                     ed.col = col;
59552                     ed.record = r;
59553                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59554                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59555                     this.activeEditor = ed;
59556                     var v = r.data[field];
59557                     ed.startEdit(this.view.getCell(row, col), v);
59558                     // combo's with 'displayField and name set
59559                     if (ed.field.displayField && ed.field.name) {
59560                         ed.field.el.dom.value = r.data[ed.field.name];
59561                     }
59562                     
59563                     
59564                 }).defer(50, this);
59565             }
59566         }
59567     },
59568         
59569     /**
59570      * Stops any active editing
59571      */
59572     stopEditing : function(){
59573         if(this.activeEditor){
59574             this.activeEditor.completeEdit();
59575         }
59576         this.activeEditor = null;
59577     },
59578         
59579          /**
59580      * Called to get grid's drag proxy text, by default returns this.ddText.
59581      * @return {String}
59582      */
59583     getDragDropText : function(){
59584         var count = this.selModel.getSelectedCell() ? 1 : 0;
59585         return String.format(this.ddText, count, count == 1 ? '' : 's');
59586     }
59587         
59588 });/*
59589  * Based on:
59590  * Ext JS Library 1.1.1
59591  * Copyright(c) 2006-2007, Ext JS, LLC.
59592  *
59593  * Originally Released Under LGPL - original licence link has changed is not relivant.
59594  *
59595  * Fork - LGPL
59596  * <script type="text/javascript">
59597  */
59598
59599 // private - not really -- you end up using it !
59600 // This is a support class used internally by the Grid components
59601
59602 /**
59603  * @class Roo.grid.GridEditor
59604  * @extends Roo.Editor
59605  * Class for creating and editable grid elements.
59606  * @param {Object} config any settings (must include field)
59607  */
59608 Roo.grid.GridEditor = function(field, config){
59609     if (!config && field.field) {
59610         config = field;
59611         field = Roo.factory(config.field, Roo.form);
59612     }
59613     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59614     field.monitorTab = false;
59615 };
59616
59617 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59618     
59619     /**
59620      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59621      */
59622     
59623     alignment: "tl-tl",
59624     autoSize: "width",
59625     hideEl : false,
59626     cls: "x-small-editor x-grid-editor",
59627     shim:false,
59628     shadow:"frame"
59629 });/*
59630  * Based on:
59631  * Ext JS Library 1.1.1
59632  * Copyright(c) 2006-2007, Ext JS, LLC.
59633  *
59634  * Originally Released Under LGPL - original licence link has changed is not relivant.
59635  *
59636  * Fork - LGPL
59637  * <script type="text/javascript">
59638  */
59639   
59640
59641   
59642 Roo.grid.PropertyRecord = Roo.data.Record.create([
59643     {name:'name',type:'string'},  'value'
59644 ]);
59645
59646
59647 Roo.grid.PropertyStore = function(grid, source){
59648     this.grid = grid;
59649     this.store = new Roo.data.Store({
59650         recordType : Roo.grid.PropertyRecord
59651     });
59652     this.store.on('update', this.onUpdate,  this);
59653     if(source){
59654         this.setSource(source);
59655     }
59656     Roo.grid.PropertyStore.superclass.constructor.call(this);
59657 };
59658
59659
59660
59661 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59662     setSource : function(o){
59663         this.source = o;
59664         this.store.removeAll();
59665         var data = [];
59666         for(var k in o){
59667             if(this.isEditableValue(o[k])){
59668                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59669             }
59670         }
59671         this.store.loadRecords({records: data}, {}, true);
59672     },
59673
59674     onUpdate : function(ds, record, type){
59675         if(type == Roo.data.Record.EDIT){
59676             var v = record.data['value'];
59677             var oldValue = record.modified['value'];
59678             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59679                 this.source[record.id] = v;
59680                 record.commit();
59681                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59682             }else{
59683                 record.reject();
59684             }
59685         }
59686     },
59687
59688     getProperty : function(row){
59689        return this.store.getAt(row);
59690     },
59691
59692     isEditableValue: function(val){
59693         if(val && val instanceof Date){
59694             return true;
59695         }else if(typeof val == 'object' || typeof val == 'function'){
59696             return false;
59697         }
59698         return true;
59699     },
59700
59701     setValue : function(prop, value){
59702         this.source[prop] = value;
59703         this.store.getById(prop).set('value', value);
59704     },
59705
59706     getSource : function(){
59707         return this.source;
59708     }
59709 });
59710
59711 Roo.grid.PropertyColumnModel = function(grid, store){
59712     this.grid = grid;
59713     var g = Roo.grid;
59714     g.PropertyColumnModel.superclass.constructor.call(this, [
59715         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59716         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59717     ]);
59718     this.store = store;
59719     this.bselect = Roo.DomHelper.append(document.body, {
59720         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59721             {tag: 'option', value: 'true', html: 'true'},
59722             {tag: 'option', value: 'false', html: 'false'}
59723         ]
59724     });
59725     Roo.id(this.bselect);
59726     var f = Roo.form;
59727     this.editors = {
59728         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59729         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59730         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59731         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59732         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59733     };
59734     this.renderCellDelegate = this.renderCell.createDelegate(this);
59735     this.renderPropDelegate = this.renderProp.createDelegate(this);
59736 };
59737
59738 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59739     
59740     
59741     nameText : 'Name',
59742     valueText : 'Value',
59743     
59744     dateFormat : 'm/j/Y',
59745     
59746     
59747     renderDate : function(dateVal){
59748         return dateVal.dateFormat(this.dateFormat);
59749     },
59750
59751     renderBool : function(bVal){
59752         return bVal ? 'true' : 'false';
59753     },
59754
59755     isCellEditable : function(colIndex, rowIndex){
59756         return colIndex == 1;
59757     },
59758
59759     getRenderer : function(col){
59760         return col == 1 ?
59761             this.renderCellDelegate : this.renderPropDelegate;
59762     },
59763
59764     renderProp : function(v){
59765         return this.getPropertyName(v);
59766     },
59767
59768     renderCell : function(val){
59769         var rv = val;
59770         if(val instanceof Date){
59771             rv = this.renderDate(val);
59772         }else if(typeof val == 'boolean'){
59773             rv = this.renderBool(val);
59774         }
59775         return Roo.util.Format.htmlEncode(rv);
59776     },
59777
59778     getPropertyName : function(name){
59779         var pn = this.grid.propertyNames;
59780         return pn && pn[name] ? pn[name] : name;
59781     },
59782
59783     getCellEditor : function(colIndex, rowIndex){
59784         var p = this.store.getProperty(rowIndex);
59785         var n = p.data['name'], val = p.data['value'];
59786         
59787         if(typeof(this.grid.customEditors[n]) == 'string'){
59788             return this.editors[this.grid.customEditors[n]];
59789         }
59790         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59791             return this.grid.customEditors[n];
59792         }
59793         if(val instanceof Date){
59794             return this.editors['date'];
59795         }else if(typeof val == 'number'){
59796             return this.editors['number'];
59797         }else if(typeof val == 'boolean'){
59798             return this.editors['boolean'];
59799         }else{
59800             return this.editors['string'];
59801         }
59802     }
59803 });
59804
59805 /**
59806  * @class Roo.grid.PropertyGrid
59807  * @extends Roo.grid.EditorGrid
59808  * This class represents the  interface of a component based property grid control.
59809  * <br><br>Usage:<pre><code>
59810  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59811       
59812  });
59813  // set any options
59814  grid.render();
59815  * </code></pre>
59816   
59817  * @constructor
59818  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59819  * The container MUST have some type of size defined for the grid to fill. The container will be
59820  * automatically set to position relative if it isn't already.
59821  * @param {Object} config A config object that sets properties on this grid.
59822  */
59823 Roo.grid.PropertyGrid = function(container, config){
59824     config = config || {};
59825     var store = new Roo.grid.PropertyStore(this);
59826     this.store = store;
59827     var cm = new Roo.grid.PropertyColumnModel(this, store);
59828     store.store.sort('name', 'ASC');
59829     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59830         ds: store.store,
59831         cm: cm,
59832         enableColLock:false,
59833         enableColumnMove:false,
59834         stripeRows:false,
59835         trackMouseOver: false,
59836         clicksToEdit:1
59837     }, config));
59838     this.getGridEl().addClass('x-props-grid');
59839     this.lastEditRow = null;
59840     this.on('columnresize', this.onColumnResize, this);
59841     this.addEvents({
59842          /**
59843              * @event beforepropertychange
59844              * Fires before a property changes (return false to stop?)
59845              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59846              * @param {String} id Record Id
59847              * @param {String} newval New Value
59848          * @param {String} oldval Old Value
59849              */
59850         "beforepropertychange": true,
59851         /**
59852              * @event propertychange
59853              * Fires after a property changes
59854              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59855              * @param {String} id Record Id
59856              * @param {String} newval New Value
59857          * @param {String} oldval Old Value
59858              */
59859         "propertychange": true
59860     });
59861     this.customEditors = this.customEditors || {};
59862 };
59863 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59864     
59865      /**
59866      * @cfg {Object} customEditors map of colnames=> custom editors.
59867      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59868      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59869      * false disables editing of the field.
59870          */
59871     
59872       /**
59873      * @cfg {Object} propertyNames map of property Names to their displayed value
59874          */
59875     
59876     render : function(){
59877         Roo.grid.PropertyGrid.superclass.render.call(this);
59878         this.autoSize.defer(100, this);
59879     },
59880
59881     autoSize : function(){
59882         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59883         if(this.view){
59884             this.view.fitColumns();
59885         }
59886     },
59887
59888     onColumnResize : function(){
59889         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59890         this.autoSize();
59891     },
59892     /**
59893      * Sets the data for the Grid
59894      * accepts a Key => Value object of all the elements avaiable.
59895      * @param {Object} data  to appear in grid.
59896      */
59897     setSource : function(source){
59898         this.store.setSource(source);
59899         //this.autoSize();
59900     },
59901     /**
59902      * Gets all the data from the grid.
59903      * @return {Object} data  data stored in grid
59904      */
59905     getSource : function(){
59906         return this.store.getSource();
59907     }
59908 });/*
59909   
59910  * Licence LGPL
59911  
59912  */
59913  
59914 /**
59915  * @class Roo.grid.Calendar
59916  * @extends Roo.util.Grid
59917  * This class extends the Grid to provide a calendar widget
59918  * <br><br>Usage:<pre><code>
59919  var grid = new Roo.grid.Calendar("my-container-id", {
59920      ds: myDataStore,
59921      cm: myColModel,
59922      selModel: mySelectionModel,
59923      autoSizeColumns: true,
59924      monitorWindowResize: false,
59925      trackMouseOver: true
59926      eventstore : real data store..
59927  });
59928  // set any options
59929  grid.render();
59930   
59931   * @constructor
59932  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59933  * The container MUST have some type of size defined for the grid to fill. The container will be
59934  * automatically set to position relative if it isn't already.
59935  * @param {Object} config A config object that sets properties on this grid.
59936  */
59937 Roo.grid.Calendar = function(container, config){
59938         // initialize the container
59939         this.container = Roo.get(container);
59940         this.container.update("");
59941         this.container.setStyle("overflow", "hidden");
59942     this.container.addClass('x-grid-container');
59943
59944     this.id = this.container.id;
59945
59946     Roo.apply(this, config);
59947     // check and correct shorthanded configs
59948     
59949     var rows = [];
59950     var d =1;
59951     for (var r = 0;r < 6;r++) {
59952         
59953         rows[r]=[];
59954         for (var c =0;c < 7;c++) {
59955             rows[r][c]= '';
59956         }
59957     }
59958     if (this.eventStore) {
59959         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59960         this.eventStore.on('load',this.onLoad, this);
59961         this.eventStore.on('beforeload',this.clearEvents, this);
59962          
59963     }
59964     
59965     this.dataSource = new Roo.data.Store({
59966             proxy: new Roo.data.MemoryProxy(rows),
59967             reader: new Roo.data.ArrayReader({}, [
59968                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59969     });
59970
59971     this.dataSource.load();
59972     this.ds = this.dataSource;
59973     this.ds.xmodule = this.xmodule || false;
59974     
59975     
59976     var cellRender = function(v,x,r)
59977     {
59978         return String.format(
59979             '<div class="fc-day  fc-widget-content"><div>' +
59980                 '<div class="fc-event-container"></div>' +
59981                 '<div class="fc-day-number">{0}</div>'+
59982                 
59983                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59984             '</div></div>', v);
59985     
59986     }
59987     
59988     
59989     this.colModel = new Roo.grid.ColumnModel( [
59990         {
59991             xtype: 'ColumnModel',
59992             xns: Roo.grid,
59993             dataIndex : 'weekday0',
59994             header : 'Sunday',
59995             renderer : cellRender
59996         },
59997         {
59998             xtype: 'ColumnModel',
59999             xns: Roo.grid,
60000             dataIndex : 'weekday1',
60001             header : 'Monday',
60002             renderer : cellRender
60003         },
60004         {
60005             xtype: 'ColumnModel',
60006             xns: Roo.grid,
60007             dataIndex : 'weekday2',
60008             header : 'Tuesday',
60009             renderer : cellRender
60010         },
60011         {
60012             xtype: 'ColumnModel',
60013             xns: Roo.grid,
60014             dataIndex : 'weekday3',
60015             header : 'Wednesday',
60016             renderer : cellRender
60017         },
60018         {
60019             xtype: 'ColumnModel',
60020             xns: Roo.grid,
60021             dataIndex : 'weekday4',
60022             header : 'Thursday',
60023             renderer : cellRender
60024         },
60025         {
60026             xtype: 'ColumnModel',
60027             xns: Roo.grid,
60028             dataIndex : 'weekday5',
60029             header : 'Friday',
60030             renderer : cellRender
60031         },
60032         {
60033             xtype: 'ColumnModel',
60034             xns: Roo.grid,
60035             dataIndex : 'weekday6',
60036             header : 'Saturday',
60037             renderer : cellRender
60038         }
60039     ]);
60040     this.cm = this.colModel;
60041     this.cm.xmodule = this.xmodule || false;
60042  
60043         
60044           
60045     //this.selModel = new Roo.grid.CellSelectionModel();
60046     //this.sm = this.selModel;
60047     //this.selModel.init(this);
60048     
60049     
60050     if(this.width){
60051         this.container.setWidth(this.width);
60052     }
60053
60054     if(this.height){
60055         this.container.setHeight(this.height);
60056     }
60057     /** @private */
60058         this.addEvents({
60059         // raw events
60060         /**
60061          * @event click
60062          * The raw click event for the entire grid.
60063          * @param {Roo.EventObject} e
60064          */
60065         "click" : true,
60066         /**
60067          * @event dblclick
60068          * The raw dblclick event for the entire grid.
60069          * @param {Roo.EventObject} e
60070          */
60071         "dblclick" : true,
60072         /**
60073          * @event contextmenu
60074          * The raw contextmenu event for the entire grid.
60075          * @param {Roo.EventObject} e
60076          */
60077         "contextmenu" : true,
60078         /**
60079          * @event mousedown
60080          * The raw mousedown event for the entire grid.
60081          * @param {Roo.EventObject} e
60082          */
60083         "mousedown" : true,
60084         /**
60085          * @event mouseup
60086          * The raw mouseup event for the entire grid.
60087          * @param {Roo.EventObject} e
60088          */
60089         "mouseup" : true,
60090         /**
60091          * @event mouseover
60092          * The raw mouseover event for the entire grid.
60093          * @param {Roo.EventObject} e
60094          */
60095         "mouseover" : true,
60096         /**
60097          * @event mouseout
60098          * The raw mouseout event for the entire grid.
60099          * @param {Roo.EventObject} e
60100          */
60101         "mouseout" : true,
60102         /**
60103          * @event keypress
60104          * The raw keypress event for the entire grid.
60105          * @param {Roo.EventObject} e
60106          */
60107         "keypress" : true,
60108         /**
60109          * @event keydown
60110          * The raw keydown event for the entire grid.
60111          * @param {Roo.EventObject} e
60112          */
60113         "keydown" : true,
60114
60115         // custom events
60116
60117         /**
60118          * @event cellclick
60119          * Fires when a cell is clicked
60120          * @param {Grid} this
60121          * @param {Number} rowIndex
60122          * @param {Number} columnIndex
60123          * @param {Roo.EventObject} e
60124          */
60125         "cellclick" : true,
60126         /**
60127          * @event celldblclick
60128          * Fires when a cell is double clicked
60129          * @param {Grid} this
60130          * @param {Number} rowIndex
60131          * @param {Number} columnIndex
60132          * @param {Roo.EventObject} e
60133          */
60134         "celldblclick" : true,
60135         /**
60136          * @event rowclick
60137          * Fires when a row is clicked
60138          * @param {Grid} this
60139          * @param {Number} rowIndex
60140          * @param {Roo.EventObject} e
60141          */
60142         "rowclick" : true,
60143         /**
60144          * @event rowdblclick
60145          * Fires when a row is double clicked
60146          * @param {Grid} this
60147          * @param {Number} rowIndex
60148          * @param {Roo.EventObject} e
60149          */
60150         "rowdblclick" : true,
60151         /**
60152          * @event headerclick
60153          * Fires when a header is clicked
60154          * @param {Grid} this
60155          * @param {Number} columnIndex
60156          * @param {Roo.EventObject} e
60157          */
60158         "headerclick" : true,
60159         /**
60160          * @event headerdblclick
60161          * Fires when a header cell is double clicked
60162          * @param {Grid} this
60163          * @param {Number} columnIndex
60164          * @param {Roo.EventObject} e
60165          */
60166         "headerdblclick" : true,
60167         /**
60168          * @event rowcontextmenu
60169          * Fires when a row is right clicked
60170          * @param {Grid} this
60171          * @param {Number} rowIndex
60172          * @param {Roo.EventObject} e
60173          */
60174         "rowcontextmenu" : true,
60175         /**
60176          * @event cellcontextmenu
60177          * Fires when a cell is right clicked
60178          * @param {Grid} this
60179          * @param {Number} rowIndex
60180          * @param {Number} cellIndex
60181          * @param {Roo.EventObject} e
60182          */
60183          "cellcontextmenu" : true,
60184         /**
60185          * @event headercontextmenu
60186          * Fires when a header is right clicked
60187          * @param {Grid} this
60188          * @param {Number} columnIndex
60189          * @param {Roo.EventObject} e
60190          */
60191         "headercontextmenu" : true,
60192         /**
60193          * @event bodyscroll
60194          * Fires when the body element is scrolled
60195          * @param {Number} scrollLeft
60196          * @param {Number} scrollTop
60197          */
60198         "bodyscroll" : true,
60199         /**
60200          * @event columnresize
60201          * Fires when the user resizes a column
60202          * @param {Number} columnIndex
60203          * @param {Number} newSize
60204          */
60205         "columnresize" : true,
60206         /**
60207          * @event columnmove
60208          * Fires when the user moves a column
60209          * @param {Number} oldIndex
60210          * @param {Number} newIndex
60211          */
60212         "columnmove" : true,
60213         /**
60214          * @event startdrag
60215          * Fires when row(s) start being dragged
60216          * @param {Grid} this
60217          * @param {Roo.GridDD} dd The drag drop object
60218          * @param {event} e The raw browser event
60219          */
60220         "startdrag" : true,
60221         /**
60222          * @event enddrag
60223          * Fires when a drag operation is complete
60224          * @param {Grid} this
60225          * @param {Roo.GridDD} dd The drag drop object
60226          * @param {event} e The raw browser event
60227          */
60228         "enddrag" : true,
60229         /**
60230          * @event dragdrop
60231          * Fires when dragged row(s) are dropped on a valid DD target
60232          * @param {Grid} this
60233          * @param {Roo.GridDD} dd The drag drop object
60234          * @param {String} targetId The target drag drop object
60235          * @param {event} e The raw browser event
60236          */
60237         "dragdrop" : true,
60238         /**
60239          * @event dragover
60240          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60241          * @param {Grid} this
60242          * @param {Roo.GridDD} dd The drag drop object
60243          * @param {String} targetId The target drag drop object
60244          * @param {event} e The raw browser event
60245          */
60246         "dragover" : true,
60247         /**
60248          * @event dragenter
60249          *  Fires when the dragged row(s) first cross another DD target while being dragged
60250          * @param {Grid} this
60251          * @param {Roo.GridDD} dd The drag drop object
60252          * @param {String} targetId The target drag drop object
60253          * @param {event} e The raw browser event
60254          */
60255         "dragenter" : true,
60256         /**
60257          * @event dragout
60258          * Fires when the dragged row(s) leave another DD target while being dragged
60259          * @param {Grid} this
60260          * @param {Roo.GridDD} dd The drag drop object
60261          * @param {String} targetId The target drag drop object
60262          * @param {event} e The raw browser event
60263          */
60264         "dragout" : true,
60265         /**
60266          * @event rowclass
60267          * Fires when a row is rendered, so you can change add a style to it.
60268          * @param {GridView} gridview   The grid view
60269          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60270          */
60271         'rowclass' : true,
60272
60273         /**
60274          * @event render
60275          * Fires when the grid is rendered
60276          * @param {Grid} grid
60277          */
60278         'render' : true,
60279             /**
60280              * @event select
60281              * Fires when a date is selected
60282              * @param {DatePicker} this
60283              * @param {Date} date The selected date
60284              */
60285         'select': true,
60286         /**
60287              * @event monthchange
60288              * Fires when the displayed month changes 
60289              * @param {DatePicker} this
60290              * @param {Date} date The selected month
60291              */
60292         'monthchange': true,
60293         /**
60294              * @event evententer
60295              * Fires when mouse over an event
60296              * @param {Calendar} this
60297              * @param {event} Event
60298              */
60299         'evententer': true,
60300         /**
60301              * @event eventleave
60302              * Fires when the mouse leaves an
60303              * @param {Calendar} this
60304              * @param {event}
60305              */
60306         'eventleave': true,
60307         /**
60308              * @event eventclick
60309              * Fires when the mouse click an
60310              * @param {Calendar} this
60311              * @param {event}
60312              */
60313         'eventclick': true,
60314         /**
60315              * @event eventrender
60316              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60317              * @param {Calendar} this
60318              * @param {data} data to be modified
60319              */
60320         'eventrender': true
60321         
60322     });
60323
60324     Roo.grid.Grid.superclass.constructor.call(this);
60325     this.on('render', function() {
60326         this.view.el.addClass('x-grid-cal'); 
60327         
60328         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60329
60330     },this);
60331     
60332     if (!Roo.grid.Calendar.style) {
60333         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60334             
60335             
60336             '.x-grid-cal .x-grid-col' :  {
60337                 height: 'auto !important',
60338                 'vertical-align': 'top'
60339             },
60340             '.x-grid-cal  .fc-event-hori' : {
60341                 height: '14px'
60342             }
60343              
60344             
60345         }, Roo.id());
60346     }
60347
60348     
60349     
60350 };
60351 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60352     /**
60353      * @cfg {Store} eventStore The store that loads events.
60354      */
60355     eventStore : 25,
60356
60357      
60358     activeDate : false,
60359     startDay : 0,
60360     autoWidth : true,
60361     monitorWindowResize : false,
60362
60363     
60364     resizeColumns : function() {
60365         var col = (this.view.el.getWidth() / 7) - 3;
60366         // loop through cols, and setWidth
60367         for(var i =0 ; i < 7 ; i++){
60368             this.cm.setColumnWidth(i, col);
60369         }
60370     },
60371      setDate :function(date) {
60372         
60373         Roo.log('setDate?');
60374         
60375         this.resizeColumns();
60376         var vd = this.activeDate;
60377         this.activeDate = date;
60378 //        if(vd && this.el){
60379 //            var t = date.getTime();
60380 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60381 //                Roo.log('using add remove');
60382 //                
60383 //                this.fireEvent('monthchange', this, date);
60384 //                
60385 //                this.cells.removeClass("fc-state-highlight");
60386 //                this.cells.each(function(c){
60387 //                   if(c.dateValue == t){
60388 //                       c.addClass("fc-state-highlight");
60389 //                       setTimeout(function(){
60390 //                            try{c.dom.firstChild.focus();}catch(e){}
60391 //                       }, 50);
60392 //                       return false;
60393 //                   }
60394 //                   return true;
60395 //                });
60396 //                return;
60397 //            }
60398 //        }
60399         
60400         var days = date.getDaysInMonth();
60401         
60402         var firstOfMonth = date.getFirstDateOfMonth();
60403         var startingPos = firstOfMonth.getDay()-this.startDay;
60404         
60405         if(startingPos < this.startDay){
60406             startingPos += 7;
60407         }
60408         
60409         var pm = date.add(Date.MONTH, -1);
60410         var prevStart = pm.getDaysInMonth()-startingPos;
60411 //        
60412         
60413         
60414         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60415         
60416         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60417         //this.cells.addClassOnOver('fc-state-hover');
60418         
60419         var cells = this.cells.elements;
60420         var textEls = this.textNodes;
60421         
60422         //Roo.each(cells, function(cell){
60423         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60424         //});
60425         
60426         days += startingPos;
60427
60428         // convert everything to numbers so it's fast
60429         var day = 86400000;
60430         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60431         //Roo.log(d);
60432         //Roo.log(pm);
60433         //Roo.log(prevStart);
60434         
60435         var today = new Date().clearTime().getTime();
60436         var sel = date.clearTime().getTime();
60437         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60438         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60439         var ddMatch = this.disabledDatesRE;
60440         var ddText = this.disabledDatesText;
60441         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60442         var ddaysText = this.disabledDaysText;
60443         var format = this.format;
60444         
60445         var setCellClass = function(cal, cell){
60446             
60447             //Roo.log('set Cell Class');
60448             cell.title = "";
60449             var t = d.getTime();
60450             
60451             //Roo.log(d);
60452             
60453             
60454             cell.dateValue = t;
60455             if(t == today){
60456                 cell.className += " fc-today";
60457                 cell.className += " fc-state-highlight";
60458                 cell.title = cal.todayText;
60459             }
60460             if(t == sel){
60461                 // disable highlight in other month..
60462                 cell.className += " fc-state-highlight";
60463                 
60464             }
60465             // disabling
60466             if(t < min) {
60467                 //cell.className = " fc-state-disabled";
60468                 cell.title = cal.minText;
60469                 return;
60470             }
60471             if(t > max) {
60472                 //cell.className = " fc-state-disabled";
60473                 cell.title = cal.maxText;
60474                 return;
60475             }
60476             if(ddays){
60477                 if(ddays.indexOf(d.getDay()) != -1){
60478                     // cell.title = ddaysText;
60479                    // cell.className = " fc-state-disabled";
60480                 }
60481             }
60482             if(ddMatch && format){
60483                 var fvalue = d.dateFormat(format);
60484                 if(ddMatch.test(fvalue)){
60485                     cell.title = ddText.replace("%0", fvalue);
60486                    cell.className = " fc-state-disabled";
60487                 }
60488             }
60489             
60490             if (!cell.initialClassName) {
60491                 cell.initialClassName = cell.dom.className;
60492             }
60493             
60494             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60495         };
60496
60497         var i = 0;
60498         
60499         for(; i < startingPos; i++) {
60500             cells[i].dayName =  (++prevStart);
60501             Roo.log(textEls[i]);
60502             d.setDate(d.getDate()+1);
60503             
60504             //cells[i].className = "fc-past fc-other-month";
60505             setCellClass(this, cells[i]);
60506         }
60507         
60508         var intDay = 0;
60509         
60510         for(; i < days; i++){
60511             intDay = i - startingPos + 1;
60512             cells[i].dayName =  (intDay);
60513             d.setDate(d.getDate()+1);
60514             
60515             cells[i].className = ''; // "x-date-active";
60516             setCellClass(this, cells[i]);
60517         }
60518         var extraDays = 0;
60519         
60520         for(; i < 42; i++) {
60521             //textEls[i].innerHTML = (++extraDays);
60522             
60523             d.setDate(d.getDate()+1);
60524             cells[i].dayName = (++extraDays);
60525             cells[i].className = "fc-future fc-other-month";
60526             setCellClass(this, cells[i]);
60527         }
60528         
60529         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60530         
60531         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60532         
60533         // this will cause all the cells to mis
60534         var rows= [];
60535         var i =0;
60536         for (var r = 0;r < 6;r++) {
60537             for (var c =0;c < 7;c++) {
60538                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60539             }    
60540         }
60541         
60542         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60543         for(i=0;i<cells.length;i++) {
60544             
60545             this.cells.elements[i].dayName = cells[i].dayName ;
60546             this.cells.elements[i].className = cells[i].className;
60547             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60548             this.cells.elements[i].title = cells[i].title ;
60549             this.cells.elements[i].dateValue = cells[i].dateValue ;
60550         }
60551         
60552         
60553         
60554         
60555         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60556         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60557         
60558         ////if(totalRows != 6){
60559             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60560            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60561        // }
60562         
60563         this.fireEvent('monthchange', this, date);
60564         
60565         
60566     },
60567  /**
60568      * Returns the grid's SelectionModel.
60569      * @return {SelectionModel}
60570      */
60571     getSelectionModel : function(){
60572         if(!this.selModel){
60573             this.selModel = new Roo.grid.CellSelectionModel();
60574         }
60575         return this.selModel;
60576     },
60577
60578     load: function() {
60579         this.eventStore.load()
60580         
60581         
60582         
60583     },
60584     
60585     findCell : function(dt) {
60586         dt = dt.clearTime().getTime();
60587         var ret = false;
60588         this.cells.each(function(c){
60589             //Roo.log("check " +c.dateValue + '?=' + dt);
60590             if(c.dateValue == dt){
60591                 ret = c;
60592                 return false;
60593             }
60594             return true;
60595         });
60596         
60597         return ret;
60598     },
60599     
60600     findCells : function(rec) {
60601         var s = rec.data.start_dt.clone().clearTime().getTime();
60602        // Roo.log(s);
60603         var e= rec.data.end_dt.clone().clearTime().getTime();
60604        // Roo.log(e);
60605         var ret = [];
60606         this.cells.each(function(c){
60607              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60608             
60609             if(c.dateValue > e){
60610                 return ;
60611             }
60612             if(c.dateValue < s){
60613                 return ;
60614             }
60615             ret.push(c);
60616         });
60617         
60618         return ret;    
60619     },
60620     
60621     findBestRow: function(cells)
60622     {
60623         var ret = 0;
60624         
60625         for (var i =0 ; i < cells.length;i++) {
60626             ret  = Math.max(cells[i].rows || 0,ret);
60627         }
60628         return ret;
60629         
60630     },
60631     
60632     
60633     addItem : function(rec)
60634     {
60635         // look for vertical location slot in
60636         var cells = this.findCells(rec);
60637         
60638         rec.row = this.findBestRow(cells);
60639         
60640         // work out the location.
60641         
60642         var crow = false;
60643         var rows = [];
60644         for(var i =0; i < cells.length; i++) {
60645             if (!crow) {
60646                 crow = {
60647                     start : cells[i],
60648                     end :  cells[i]
60649                 };
60650                 continue;
60651             }
60652             if (crow.start.getY() == cells[i].getY()) {
60653                 // on same row.
60654                 crow.end = cells[i];
60655                 continue;
60656             }
60657             // different row.
60658             rows.push(crow);
60659             crow = {
60660                 start: cells[i],
60661                 end : cells[i]
60662             };
60663             
60664         }
60665         
60666         rows.push(crow);
60667         rec.els = [];
60668         rec.rows = rows;
60669         rec.cells = cells;
60670         for (var i = 0; i < cells.length;i++) {
60671             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60672             
60673         }
60674         
60675         
60676     },
60677     
60678     clearEvents: function() {
60679         
60680         if (!this.eventStore.getCount()) {
60681             return;
60682         }
60683         // reset number of rows in cells.
60684         Roo.each(this.cells.elements, function(c){
60685             c.rows = 0;
60686         });
60687         
60688         this.eventStore.each(function(e) {
60689             this.clearEvent(e);
60690         },this);
60691         
60692     },
60693     
60694     clearEvent : function(ev)
60695     {
60696         if (ev.els) {
60697             Roo.each(ev.els, function(el) {
60698                 el.un('mouseenter' ,this.onEventEnter, this);
60699                 el.un('mouseleave' ,this.onEventLeave, this);
60700                 el.remove();
60701             },this);
60702             ev.els = [];
60703         }
60704     },
60705     
60706     
60707     renderEvent : function(ev,ctr) {
60708         if (!ctr) {
60709              ctr = this.view.el.select('.fc-event-container',true).first();
60710         }
60711         
60712          
60713         this.clearEvent(ev);
60714             //code
60715        
60716         
60717         
60718         ev.els = [];
60719         var cells = ev.cells;
60720         var rows = ev.rows;
60721         this.fireEvent('eventrender', this, ev);
60722         
60723         for(var i =0; i < rows.length; i++) {
60724             
60725             cls = '';
60726             if (i == 0) {
60727                 cls += ' fc-event-start';
60728             }
60729             if ((i+1) == rows.length) {
60730                 cls += ' fc-event-end';
60731             }
60732             
60733             //Roo.log(ev.data);
60734             // how many rows should it span..
60735             var cg = this.eventTmpl.append(ctr,Roo.apply({
60736                 fccls : cls
60737                 
60738             }, ev.data) , true);
60739             
60740             
60741             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60742             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60743             cg.on('click', this.onEventClick, this, ev);
60744             
60745             ev.els.push(cg);
60746             
60747             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60748             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60749             //Roo.log(cg);
60750              
60751             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60752             cg.setWidth(ebox.right - sbox.x -2);
60753         }
60754     },
60755     
60756     renderEvents: function()
60757     {   
60758         // first make sure there is enough space..
60759         
60760         if (!this.eventTmpl) {
60761             this.eventTmpl = new Roo.Template(
60762                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60763                     '<div class="fc-event-inner">' +
60764                         '<span class="fc-event-time">{time}</span>' +
60765                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60766                     '</div>' +
60767                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60768                 '</div>'
60769             );
60770                 
60771         }
60772                
60773         
60774         
60775         this.cells.each(function(c) {
60776             //Roo.log(c.select('.fc-day-content div',true).first());
60777             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60778         });
60779         
60780         var ctr = this.view.el.select('.fc-event-container',true).first();
60781         
60782         var cls;
60783         this.eventStore.each(function(ev){
60784             
60785             this.renderEvent(ev);
60786              
60787              
60788         }, this);
60789         this.view.layout();
60790         
60791     },
60792     
60793     onEventEnter: function (e, el,event,d) {
60794         this.fireEvent('evententer', this, el, event);
60795     },
60796     
60797     onEventLeave: function (e, el,event,d) {
60798         this.fireEvent('eventleave', this, el, event);
60799     },
60800     
60801     onEventClick: function (e, el,event,d) {
60802         this.fireEvent('eventclick', this, el, event);
60803     },
60804     
60805     onMonthChange: function () {
60806         this.store.load();
60807     },
60808     
60809     onLoad: function () {
60810         
60811         //Roo.log('calendar onload');
60812 //         
60813         if(this.eventStore.getCount() > 0){
60814             
60815            
60816             
60817             this.eventStore.each(function(d){
60818                 
60819                 
60820                 // FIXME..
60821                 var add =   d.data;
60822                 if (typeof(add.end_dt) == 'undefined')  {
60823                     Roo.log("Missing End time in calendar data: ");
60824                     Roo.log(d);
60825                     return;
60826                 }
60827                 if (typeof(add.start_dt) == 'undefined')  {
60828                     Roo.log("Missing Start time in calendar data: ");
60829                     Roo.log(d);
60830                     return;
60831                 }
60832                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60833                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60834                 add.id = add.id || d.id;
60835                 add.title = add.title || '??';
60836                 
60837                 this.addItem(d);
60838                 
60839              
60840             },this);
60841         }
60842         
60843         this.renderEvents();
60844     }
60845     
60846
60847 });
60848 /*
60849  grid : {
60850                 xtype: 'Grid',
60851                 xns: Roo.grid,
60852                 listeners : {
60853                     render : function ()
60854                     {
60855                         _this.grid = this;
60856                         
60857                         if (!this.view.el.hasClass('course-timesheet')) {
60858                             this.view.el.addClass('course-timesheet');
60859                         }
60860                         if (this.tsStyle) {
60861                             this.ds.load({});
60862                             return; 
60863                         }
60864                         Roo.log('width');
60865                         Roo.log(_this.grid.view.el.getWidth());
60866                         
60867                         
60868                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60869                             '.course-timesheet .x-grid-row' : {
60870                                 height: '80px'
60871                             },
60872                             '.x-grid-row td' : {
60873                                 'vertical-align' : 0
60874                             },
60875                             '.course-edit-link' : {
60876                                 'color' : 'blue',
60877                                 'text-overflow' : 'ellipsis',
60878                                 'overflow' : 'hidden',
60879                                 'white-space' : 'nowrap',
60880                                 'cursor' : 'pointer'
60881                             },
60882                             '.sub-link' : {
60883                                 'color' : 'green'
60884                             },
60885                             '.de-act-sup-link' : {
60886                                 'color' : 'purple',
60887                                 'text-decoration' : 'line-through'
60888                             },
60889                             '.de-act-link' : {
60890                                 'color' : 'red',
60891                                 'text-decoration' : 'line-through'
60892                             },
60893                             '.course-timesheet .course-highlight' : {
60894                                 'border-top-style': 'dashed !important',
60895                                 'border-bottom-bottom': 'dashed !important'
60896                             },
60897                             '.course-timesheet .course-item' : {
60898                                 'font-family'   : 'tahoma, arial, helvetica',
60899                                 'font-size'     : '11px',
60900                                 'overflow'      : 'hidden',
60901                                 'padding-left'  : '10px',
60902                                 'padding-right' : '10px',
60903                                 'padding-top' : '10px' 
60904                             }
60905                             
60906                         }, Roo.id());
60907                                 this.ds.load({});
60908                     }
60909                 },
60910                 autoWidth : true,
60911                 monitorWindowResize : false,
60912                 cellrenderer : function(v,x,r)
60913                 {
60914                     return v;
60915                 },
60916                 sm : {
60917                     xtype: 'CellSelectionModel',
60918                     xns: Roo.grid
60919                 },
60920                 dataSource : {
60921                     xtype: 'Store',
60922                     xns: Roo.data,
60923                     listeners : {
60924                         beforeload : function (_self, options)
60925                         {
60926                             options.params = options.params || {};
60927                             options.params._month = _this.monthField.getValue();
60928                             options.params.limit = 9999;
60929                             options.params['sort'] = 'when_dt';    
60930                             options.params['dir'] = 'ASC';    
60931                             this.proxy.loadResponse = this.loadResponse;
60932                             Roo.log("load?");
60933                             //this.addColumns();
60934                         },
60935                         load : function (_self, records, options)
60936                         {
60937                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60938                                 // if you click on the translation.. you can edit it...
60939                                 var el = Roo.get(this);
60940                                 var id = el.dom.getAttribute('data-id');
60941                                 var d = el.dom.getAttribute('data-date');
60942                                 var t = el.dom.getAttribute('data-time');
60943                                 //var id = this.child('span').dom.textContent;
60944                                 
60945                                 //Roo.log(this);
60946                                 Pman.Dialog.CourseCalendar.show({
60947                                     id : id,
60948                                     when_d : d,
60949                                     when_t : t,
60950                                     productitem_active : id ? 1 : 0
60951                                 }, function() {
60952                                     _this.grid.ds.load({});
60953                                 });
60954                            
60955                            });
60956                            
60957                            _this.panel.fireEvent('resize', [ '', '' ]);
60958                         }
60959                     },
60960                     loadResponse : function(o, success, response){
60961                             // this is overridden on before load..
60962                             
60963                             Roo.log("our code?");       
60964                             //Roo.log(success);
60965                             //Roo.log(response)
60966                             delete this.activeRequest;
60967                             if(!success){
60968                                 this.fireEvent("loadexception", this, o, response);
60969                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60970                                 return;
60971                             }
60972                             var result;
60973                             try {
60974                                 result = o.reader.read(response);
60975                             }catch(e){
60976                                 Roo.log("load exception?");
60977                                 this.fireEvent("loadexception", this, o, response, e);
60978                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60979                                 return;
60980                             }
60981                             Roo.log("ready...");        
60982                             // loop through result.records;
60983                             // and set this.tdate[date] = [] << array of records..
60984                             _this.tdata  = {};
60985                             Roo.each(result.records, function(r){
60986                                 //Roo.log(r.data);
60987                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60988                                     _this.tdata[r.data.when_dt.format('j')] = [];
60989                                 }
60990                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60991                             });
60992                             
60993                             //Roo.log(_this.tdata);
60994                             
60995                             result.records = [];
60996                             result.totalRecords = 6;
60997                     
60998                             // let's generate some duumy records for the rows.
60999                             //var st = _this.dateField.getValue();
61000                             
61001                             // work out monday..
61002                             //st = st.add(Date.DAY, -1 * st.format('w'));
61003                             
61004                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61005                             
61006                             var firstOfMonth = date.getFirstDayOfMonth();
61007                             var days = date.getDaysInMonth();
61008                             var d = 1;
61009                             var firstAdded = false;
61010                             for (var i = 0; i < result.totalRecords ; i++) {
61011                                 //var d= st.add(Date.DAY, i);
61012                                 var row = {};
61013                                 var added = 0;
61014                                 for(var w = 0 ; w < 7 ; w++){
61015                                     if(!firstAdded && firstOfMonth != w){
61016                                         continue;
61017                                     }
61018                                     if(d > days){
61019                                         continue;
61020                                     }
61021                                     firstAdded = true;
61022                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61023                                     row['weekday'+w] = String.format(
61024                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61025                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61026                                                     d,
61027                                                     date.format('Y-m-')+dd
61028                                                 );
61029                                     added++;
61030                                     if(typeof(_this.tdata[d]) != 'undefined'){
61031                                         Roo.each(_this.tdata[d], function(r){
61032                                             var is_sub = '';
61033                                             var deactive = '';
61034                                             var id = r.id;
61035                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61036                                             if(r.parent_id*1>0){
61037                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61038                                                 id = r.parent_id;
61039                                             }
61040                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61041                                                 deactive = 'de-act-link';
61042                                             }
61043                                             
61044                                             row['weekday'+w] += String.format(
61045                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61046                                                     id, //0
61047                                                     r.product_id_name, //1
61048                                                     r.when_dt.format('h:ia'), //2
61049                                                     is_sub, //3
61050                                                     deactive, //4
61051                                                     desc // 5
61052                                             );
61053                                         });
61054                                     }
61055                                     d++;
61056                                 }
61057                                 
61058                                 // only do this if something added..
61059                                 if(added > 0){ 
61060                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61061                                 }
61062                                 
61063                                 
61064                                 // push it twice. (second one with an hour..
61065                                 
61066                             }
61067                             //Roo.log(result);
61068                             this.fireEvent("load", this, o, o.request.arg);
61069                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61070                         },
61071                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61072                     proxy : {
61073                         xtype: 'HttpProxy',
61074                         xns: Roo.data,
61075                         method : 'GET',
61076                         url : baseURL + '/Roo/Shop_course.php'
61077                     },
61078                     reader : {
61079                         xtype: 'JsonReader',
61080                         xns: Roo.data,
61081                         id : 'id',
61082                         fields : [
61083                             {
61084                                 'name': 'id',
61085                                 'type': 'int'
61086                             },
61087                             {
61088                                 'name': 'when_dt',
61089                                 'type': 'string'
61090                             },
61091                             {
61092                                 'name': 'end_dt',
61093                                 'type': 'string'
61094                             },
61095                             {
61096                                 'name': 'parent_id',
61097                                 'type': 'int'
61098                             },
61099                             {
61100                                 'name': 'product_id',
61101                                 'type': 'int'
61102                             },
61103                             {
61104                                 'name': 'productitem_id',
61105                                 'type': 'int'
61106                             },
61107                             {
61108                                 'name': 'guid',
61109                                 'type': 'int'
61110                             }
61111                         ]
61112                     }
61113                 },
61114                 toolbar : {
61115                     xtype: 'Toolbar',
61116                     xns: Roo,
61117                     items : [
61118                         {
61119                             xtype: 'Button',
61120                             xns: Roo.Toolbar,
61121                             listeners : {
61122                                 click : function (_self, e)
61123                                 {
61124                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61125                                     sd.setMonth(sd.getMonth()-1);
61126                                     _this.monthField.setValue(sd.format('Y-m-d'));
61127                                     _this.grid.ds.load({});
61128                                 }
61129                             },
61130                             text : "Back"
61131                         },
61132                         {
61133                             xtype: 'Separator',
61134                             xns: Roo.Toolbar
61135                         },
61136                         {
61137                             xtype: 'MonthField',
61138                             xns: Roo.form,
61139                             listeners : {
61140                                 render : function (_self)
61141                                 {
61142                                     _this.monthField = _self;
61143                                    // _this.monthField.set  today
61144                                 },
61145                                 select : function (combo, date)
61146                                 {
61147                                     _this.grid.ds.load({});
61148                                 }
61149                             },
61150                             value : (function() { return new Date(); })()
61151                         },
61152                         {
61153                             xtype: 'Separator',
61154                             xns: Roo.Toolbar
61155                         },
61156                         {
61157                             xtype: 'TextItem',
61158                             xns: Roo.Toolbar,
61159                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61160                         },
61161                         {
61162                             xtype: 'Fill',
61163                             xns: Roo.Toolbar
61164                         },
61165                         {
61166                             xtype: 'Button',
61167                             xns: Roo.Toolbar,
61168                             listeners : {
61169                                 click : function (_self, e)
61170                                 {
61171                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61172                                     sd.setMonth(sd.getMonth()+1);
61173                                     _this.monthField.setValue(sd.format('Y-m-d'));
61174                                     _this.grid.ds.load({});
61175                                 }
61176                             },
61177                             text : "Next"
61178                         }
61179                     ]
61180                 },
61181                  
61182             }
61183         };
61184         
61185         *//*
61186  * Based on:
61187  * Ext JS Library 1.1.1
61188  * Copyright(c) 2006-2007, Ext JS, LLC.
61189  *
61190  * Originally Released Under LGPL - original licence link has changed is not relivant.
61191  *
61192  * Fork - LGPL
61193  * <script type="text/javascript">
61194  */
61195  
61196 /**
61197  * @class Roo.LoadMask
61198  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61199  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61200  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61201  * element's UpdateManager load indicator and will be destroyed after the initial load.
61202  * @constructor
61203  * Create a new LoadMask
61204  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61205  * @param {Object} config The config object
61206  */
61207 Roo.LoadMask = function(el, config){
61208     this.el = Roo.get(el);
61209     Roo.apply(this, config);
61210     if(this.store){
61211         this.store.on('beforeload', this.onBeforeLoad, this);
61212         this.store.on('load', this.onLoad, this);
61213         this.store.on('loadexception', this.onLoadException, this);
61214         this.removeMask = false;
61215     }else{
61216         var um = this.el.getUpdateManager();
61217         um.showLoadIndicator = false; // disable the default indicator
61218         um.on('beforeupdate', this.onBeforeLoad, this);
61219         um.on('update', this.onLoad, this);
61220         um.on('failure', this.onLoad, this);
61221         this.removeMask = true;
61222     }
61223 };
61224
61225 Roo.LoadMask.prototype = {
61226     /**
61227      * @cfg {Boolean} removeMask
61228      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61229      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61230      */
61231     /**
61232      * @cfg {String} msg
61233      * The text to display in a centered loading message box (defaults to 'Loading...')
61234      */
61235     msg : 'Loading...',
61236     /**
61237      * @cfg {String} msgCls
61238      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61239      */
61240     msgCls : 'x-mask-loading',
61241
61242     /**
61243      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61244      * @type Boolean
61245      */
61246     disabled: false,
61247
61248     /**
61249      * Disables the mask to prevent it from being displayed
61250      */
61251     disable : function(){
61252        this.disabled = true;
61253     },
61254
61255     /**
61256      * Enables the mask so that it can be displayed
61257      */
61258     enable : function(){
61259         this.disabled = false;
61260     },
61261     
61262     onLoadException : function()
61263     {
61264         Roo.log(arguments);
61265         
61266         if (typeof(arguments[3]) != 'undefined') {
61267             Roo.MessageBox.alert("Error loading",arguments[3]);
61268         } 
61269         /*
61270         try {
61271             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61272                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61273             }   
61274         } catch(e) {
61275             
61276         }
61277         */
61278     
61279         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61280     },
61281     // private
61282     onLoad : function()
61283     {
61284         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61285     },
61286
61287     // private
61288     onBeforeLoad : function(){
61289         if(!this.disabled){
61290             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61291         }
61292     },
61293
61294     // private
61295     destroy : function(){
61296         if(this.store){
61297             this.store.un('beforeload', this.onBeforeLoad, this);
61298             this.store.un('load', this.onLoad, this);
61299             this.store.un('loadexception', this.onLoadException, this);
61300         }else{
61301             var um = this.el.getUpdateManager();
61302             um.un('beforeupdate', this.onBeforeLoad, this);
61303             um.un('update', this.onLoad, this);
61304             um.un('failure', this.onLoad, this);
61305         }
61306     }
61307 };/*
61308  * Based on:
61309  * Ext JS Library 1.1.1
61310  * Copyright(c) 2006-2007, Ext JS, LLC.
61311  *
61312  * Originally Released Under LGPL - original licence link has changed is not relivant.
61313  *
61314  * Fork - LGPL
61315  * <script type="text/javascript">
61316  */
61317
61318
61319 /**
61320  * @class Roo.XTemplate
61321  * @extends Roo.Template
61322  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61323 <pre><code>
61324 var t = new Roo.XTemplate(
61325         '&lt;select name="{name}"&gt;',
61326                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61327         '&lt;/select&gt;'
61328 );
61329  
61330 // then append, applying the master template values
61331  </code></pre>
61332  *
61333  * Supported features:
61334  *
61335  *  Tags:
61336
61337 <pre><code>
61338       {a_variable} - output encoded.
61339       {a_variable.format:("Y-m-d")} - call a method on the variable
61340       {a_variable:raw} - unencoded output
61341       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61342       {a_variable:this.method_on_template(...)} - call a method on the template object.
61343  
61344 </code></pre>
61345  *  The tpl tag:
61346 <pre><code>
61347         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61348         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61349         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61350         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61351   
61352         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61353         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61354 </code></pre>
61355  *      
61356  */
61357 Roo.XTemplate = function()
61358 {
61359     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61360     if (this.html) {
61361         this.compile();
61362     }
61363 };
61364
61365
61366 Roo.extend(Roo.XTemplate, Roo.Template, {
61367
61368     /**
61369      * The various sub templates
61370      */
61371     tpls : false,
61372     /**
61373      *
61374      * basic tag replacing syntax
61375      * WORD:WORD()
61376      *
61377      * // you can fake an object call by doing this
61378      *  x.t:(test,tesT) 
61379      * 
61380      */
61381     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61382
61383     /**
61384      * compile the template
61385      *
61386      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61387      *
61388      */
61389     compile: function()
61390     {
61391         var s = this.html;
61392      
61393         s = ['<tpl>', s, '</tpl>'].join('');
61394     
61395         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61396             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61397             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61398             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61399             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61400             m,
61401             id     = 0,
61402             tpls   = [];
61403     
61404         while(true == !!(m = s.match(re))){
61405             var forMatch   = m[0].match(nameRe),
61406                 ifMatch   = m[0].match(ifRe),
61407                 execMatch   = m[0].match(execRe),
61408                 namedMatch   = m[0].match(namedRe),
61409                 
61410                 exp  = null, 
61411                 fn   = null,
61412                 exec = null,
61413                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61414                 
61415             if (ifMatch) {
61416                 // if - puts fn into test..
61417                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61418                 if(exp){
61419                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61420                 }
61421             }
61422             
61423             if (execMatch) {
61424                 // exec - calls a function... returns empty if true is  returned.
61425                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61426                 if(exp){
61427                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61428                 }
61429             }
61430             
61431             
61432             if (name) {
61433                 // for = 
61434                 switch(name){
61435                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61436                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61437                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61438                 }
61439             }
61440             var uid = namedMatch ? namedMatch[1] : id;
61441             
61442             
61443             tpls.push({
61444                 id:     namedMatch ? namedMatch[1] : id,
61445                 target: name,
61446                 exec:   exec,
61447                 test:   fn,
61448                 body:   m[1] || ''
61449             });
61450             if (namedMatch) {
61451                 s = s.replace(m[0], '');
61452             } else { 
61453                 s = s.replace(m[0], '{xtpl'+ id + '}');
61454             }
61455             ++id;
61456         }
61457         this.tpls = [];
61458         for(var i = tpls.length-1; i >= 0; --i){
61459             this.compileTpl(tpls[i]);
61460             this.tpls[tpls[i].id] = tpls[i];
61461         }
61462         this.master = tpls[tpls.length-1];
61463         return this;
61464     },
61465     /**
61466      * same as applyTemplate, except it's done to one of the subTemplates
61467      * when using named templates, you can do:
61468      *
61469      * var str = pl.applySubTemplate('your-name', values);
61470      *
61471      * 
61472      * @param {Number} id of the template
61473      * @param {Object} values to apply to template
61474      * @param {Object} parent (normaly the instance of this object)
61475      */
61476     applySubTemplate : function(id, values, parent)
61477     {
61478         
61479         
61480         var t = this.tpls[id];
61481         
61482         
61483         try { 
61484             if(t.test && !t.test.call(this, values, parent)){
61485                 return '';
61486             }
61487         } catch(e) {
61488             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61489             Roo.log(e.toString());
61490             Roo.log(t.test);
61491             return ''
61492         }
61493         try { 
61494             
61495             if(t.exec && t.exec.call(this, values, parent)){
61496                 return '';
61497             }
61498         } catch(e) {
61499             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61500             Roo.log(e.toString());
61501             Roo.log(t.exec);
61502             return ''
61503         }
61504         try {
61505             var vs = t.target ? t.target.call(this, values, parent) : values;
61506             parent = t.target ? values : parent;
61507             if(t.target && vs instanceof Array){
61508                 var buf = [];
61509                 for(var i = 0, len = vs.length; i < len; i++){
61510                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61511                 }
61512                 return buf.join('');
61513             }
61514             return t.compiled.call(this, vs, parent);
61515         } catch (e) {
61516             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61517             Roo.log(e.toString());
61518             Roo.log(t.compiled);
61519             return '';
61520         }
61521     },
61522
61523     compileTpl : function(tpl)
61524     {
61525         var fm = Roo.util.Format;
61526         var useF = this.disableFormats !== true;
61527         var sep = Roo.isGecko ? "+" : ",";
61528         var undef = function(str) {
61529             Roo.log("Property not found :"  + str);
61530             return '';
61531         };
61532         
61533         var fn = function(m, name, format, args)
61534         {
61535             //Roo.log(arguments);
61536             args = args ? args.replace(/\\'/g,"'") : args;
61537             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61538             if (typeof(format) == 'undefined') {
61539                 format= 'htmlEncode';
61540             }
61541             if (format == 'raw' ) {
61542                 format = false;
61543             }
61544             
61545             if(name.substr(0, 4) == 'xtpl'){
61546                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61547             }
61548             
61549             // build an array of options to determine if value is undefined..
61550             
61551             // basically get 'xxxx.yyyy' then do
61552             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61553             //    (function () { Roo.log("Property not found"); return ''; })() :
61554             //    ......
61555             
61556             var udef_ar = [];
61557             var lookfor = '';
61558             Roo.each(name.split('.'), function(st) {
61559                 lookfor += (lookfor.length ? '.': '') + st;
61560                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61561             });
61562             
61563             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61564             
61565             
61566             if(format && useF){
61567                 
61568                 args = args ? ',' + args : "";
61569                  
61570                 if(format.substr(0, 5) != "this."){
61571                     format = "fm." + format + '(';
61572                 }else{
61573                     format = 'this.call("'+ format.substr(5) + '", ';
61574                     args = ", values";
61575                 }
61576                 
61577                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61578             }
61579              
61580             if (args.length) {
61581                 // called with xxyx.yuu:(test,test)
61582                 // change to ()
61583                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61584             }
61585             // raw.. - :raw modifier..
61586             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61587             
61588         };
61589         var body;
61590         // branched to use + in gecko and [].join() in others
61591         if(Roo.isGecko){
61592             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61593                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61594                     "';};};";
61595         }else{
61596             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61597             body.push(tpl.body.replace(/(\r\n|\n)/g,
61598                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61599             body.push("'].join('');};};");
61600             body = body.join('');
61601         }
61602         
61603         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61604        
61605         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61606         eval(body);
61607         
61608         return this;
61609     },
61610
61611     applyTemplate : function(values){
61612         return this.master.compiled.call(this, values, {});
61613         //var s = this.subs;
61614     },
61615
61616     apply : function(){
61617         return this.applyTemplate.apply(this, arguments);
61618     }
61619
61620  });
61621
61622 Roo.XTemplate.from = function(el){
61623     el = Roo.getDom(el);
61624     return new Roo.XTemplate(el.value || el.innerHTML);
61625 };